Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions res/qml/FocusedWidgetControl.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import Mixxx 0.1 as Mixxx

Mixxx.ControlProxy {

enum WidgetKind {
None,
Searchbar,
Sidebar,
LibraryView
}

group: "[Library]"
key: "focused_widget"
}
91 changes: 85 additions & 6 deletions res/qml/Library.qml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "." as Skin
import Mixxx 0.1 as Mixxx
import QtQml.Models 2.12
import QtQuick 2.12
import "Theme"

Expand All @@ -8,22 +9,89 @@ Item {
color: Theme.deckBackgroundColor
anchors.fill: parent

LibraryControl {
id: libraryControl

onMoveVertical: listView.moveSelectionVertical(offset)
onLoadSelectedTrack: listView.loadSelectedTrack(group, play)
onLoadSelectedTrackIntoNextAvailableDeck: listView.loadSelectedTrackIntoNextAvailableDeck(play)
onFocusWidgetChanged: {
switch (focusWidget) {
case FocusedWidgetControl.WidgetKind.LibraryView:
listView.forceActiveFocus();
break;
}
}
}

ListView {
id: listView

function moveSelectionVertical(value) {
if (value == 0)
return ;

const rowCount = model.rowCount();
if (rowCount == 0)
return ;

currentIndex = Mixxx.MathUtils.positiveModulo(currentIndex + value, rowCount);
}

function loadSelectedTrackIntoNextAvailableDeck(play) {
const url = model.get(currentIndex).fileUrl;
if (!url)
return ;

Mixxx.PlayerManager.loadLocationUrlIntoNextAvailableDeck(url, play);
}

function loadSelectedTrack(group, play) {
const url = model.get(currentIndex).fileUrl;
if (!url)
return ;

const player = Mixxx.PlayerManager.getPlayer(group);
if (!player)
return ;

player.loadTrackFromLocationUrl(url, play);
}

anchors.fill: parent
anchors.margins: 10
clip: true
keyNavigationWraps: true
highlightMoveDuration: 250
highlightResizeDuration: 50
model: Mixxx.Library.model
Keys.onPressed: {
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
listView.loadSelectedTrackIntoNextAvailableDeck(false);
break;
}
}

delegate: Item {
id: itemDelegate

implicitWidth: 300
implicitWidth: listView.width
implicitHeight: 30

Text {
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
text: artist + " - " + title
color: Theme.deckTextColor
color: (listView.currentIndex == index && listView.activeFocus) ? Theme.blue : Theme.deckTextColor

Behavior on color {
ColorAnimation {
duration: listView.highlightMoveDuration
}

}

}

Image {
Expand All @@ -44,13 +112,24 @@ Item {

anchors.fill: parent
drag.target: dragItem
onPressed: parent.grabToImage((result) => {
dragItem.Drag.imageSource = result.url;
})
onPressed: {
listView.forceActiveFocus();
listView.currentIndex = index;
parent.grabToImage((result) => {
dragItem.Drag.imageSource = result.url;
});
}
onDoubleClicked: listView.loadSelectedTrackIntoNextAvailableDeck(false)
}

}

highlight: Rectangle {
border.color: listView.activeFocus ? Theme.blue : Theme.deckTextColor
border.width: 1
color: "transparent"
}

}

}
Expand Down
156 changes: 156 additions & 0 deletions res/qml/LibraryControl.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import Mixxx 0.1 as Mixxx
import QtQuick 2.12

Item {
Comment thread
Swiftb0y marked this conversation as resolved.
id: root

property alias focusWidget: focusedWidgetControl.value

signal moveVertical(int offset)
signal loadSelectedTrack(string group, bool play)
signal loadSelectedTrackIntoNextAvailableDeck(bool play)

FocusedWidgetControl {
id: focusedWidgetControl

Component.onCompleted: this.value = FocusedWidgetControl.WidgetKind.LibraryView
}

Mixxx.ControlProxy {
group: "[Library]"
key: "GoToItem"
onValueChanged: {
if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView)
root.loadSelectedTrackIntoNextAvailableDeck(false);

}
}

Mixxx.ControlProxy {
group: "[Playlist]"
key: "LoadSelectedIntoFirstStopped"
onValueChanged: {
if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView)
root.loadSelectedTrackIntoNextAvailableDeck(false);

}
}

Mixxx.ControlProxy {
group: "[Playlist]"
key: "SelectTrackKnob"
onValueChanged: {
if (value != 0) {
root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView;
root.moveVertical(value);
}
}
}

Mixxx.ControlProxy {
group: "[Playlist]"
key: "SelectPrevTrack"
onValueChanged: {
if (value != 0) {
root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView;
root.moveVertical(-1);
}
}
}

Mixxx.ControlProxy {
group: "[Playlist]"
key: "SelectNextTrack"
onValueChanged: {
if (value != 0) {
root.focusWidget = FocusedWidgetControl.WidgetKind.LibraryView;
root.moveVertical(1);
}
}
}

Mixxx.ControlProxy {
group: "[Library]"
key: "MoveVertical"
onValueChanged: {
if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView)
root.moveVertical(value);

}
}

Mixxx.ControlProxy {
group: "[Library]"
key: "MoveUp"
onValueChanged: {
if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView)
root.moveVertical(-1);

}
}

Mixxx.ControlProxy {
group: "[Library]"
key: "MoveDown"
onValueChanged: {
if (value != 0 && root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView)
root.moveVertical(1);

}
}

Mixxx.ControlProxy {
id: numDecksControl

group: "[Master]"
key: "num_decks"
}

Instantiator {
model: numDecksControl.value

delegate: LibraryControlLoadSelectedTrackHandler {
group: "[Channel" + (index + 1) + "]"
enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView
onLoadTrackRequested: root.loadSelectedTrack(group, play)
}

}

Mixxx.ControlProxy {
id: numPreviewDecksControl

group: "[Master]"
key: "num_preview_decks"
}

Instantiator {
model: numPreviewDecksControl.value

delegate: LibraryControlLoadSelectedTrackHandler {
group: "[PreviewDeck" + (index + 1) + "]"
enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView
onLoadTrackRequested: root.loadSelectedTrack(group, play)
}

}

Mixxx.ControlProxy {
id: numSamplersControl

group: "[Master]"
key: "num_samplers"
}

Instantiator {
model: numSamplersControl.value

delegate: LibraryControlLoadSelectedTrackHandler {
group: "[Sampler" + (index + 1) + "]"
enabled: root.focusWidget == FocusedWidgetControl.WidgetKind.LibraryView
onLoadTrackRequested: root.loadSelectedTrack(group, play)
}

}

}
38 changes: 38 additions & 0 deletions res/qml/LibraryControlLoadSelectedTrackHandler.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Mixxx 0.1 as Mixxx
import QtQuick 2.12

/// Usually, this component shouldn't be an (visual) `Item` and use something
/// like `QtObject` instead. However, for some reason using `QtObject` here
/// makes Mixxx crash on load (using Qt 5.15.2+kde+r43-1). We can check if this
/// is fixed upstream once we switch to Qt 6.
Item {
id: root

property string group // required
property bool enabled: true

signal loadTrackRequested(bool play)

Mixxx.ControlProxy {
group: root.group
key: "LoadSelectedTrack"
onValueChanged: {
if (value == 0 || !root.enabled)
Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A QtObject does not have an enabled property. Receiving keyboard and mouse events is a QtQuick feature and thus managed through the QtQuick qml base type Item. This is probably why the QtObject version breaks. Maybe because its breaking on evaluating !undefined since root.enabled is undefined?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also if removing the Item dependency still didn't fix it, maybe we're using QtObject wrong and the base should be a Component instead?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item doesn't have an enabled property either, I was declared here. And in any case, it should throw a warning, not cause a segfault. Component doesn't work either, same issue as with QtObject.

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y Jan 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was declared here

Sorry, I missed that, still Item does have an enabled property. I guess the declared one shadows the Items one. https://doc.qt.io/qt-5/qml-qtquick-item.html#enabled-prop

And in any case, it should throw a warning, not cause a segfault.

Yes, of course. The crash is still a bug, though I thought I might have found the issue thats triggering the bug and we could work around it, but I was wrong.

return ;

root.loadTrackRequested(false);
}
}

Mixxx.ControlProxy {
group: root.group
key: "LoadSelectedTrackAndPlay"
onValueChanged: {
if (value == 0 || !root.enabled)
return ;

root.loadTrackRequested(true);
}
}

}
14 changes: 14 additions & 0 deletions res/qml/Mixxx/MathUtils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,17 @@
export const clamp = function(value, min, max) {
return Math.max(Math.min(value, max), min);
};

/**
* @param {number} x Value
* @param {number} m Modulus
* @returns {number} Result of y where y = x modulo m and y > 0
*/
export const positiveModulo = function(x, m) {
console.assert(m > 0);
let result = x % m;
while (result < 0) {
result += m;
}
return result;
};
4 changes: 2 additions & 2 deletions src/library/library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,11 @@ void Library::slotLoadTrack(TrackPointer pTrack) {
emit loadTrack(pTrack);
}

void Library::slotLoadLocationToPlayer(const QString& location, const QString& group) {
void Library::slotLoadLocationToPlayer(const QString& location, const QString& group, bool play) {
auto trackRef = TrackRef::fromFilePath(location);
TrackPointer pTrack = m_pTrackCollectionManager->getOrAddTrack(trackRef);
if (pTrack) {
emit loadTrackToPlayer(pTrack, group);
emit loadTrackToPlayer(pTrack, group, play);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/library/library.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class Library: public QObject {
void slotSwitchToView(const QString& view);
void slotLoadTrack(TrackPointer pTrack);
void slotLoadTrackToPlayer(TrackPointer pTrack, const QString& group, bool play);
void slotLoadLocationToPlayer(const QString& location, const QString& group);
void slotLoadLocationToPlayer(const QString& location, const QString& group, bool play);
void slotRefreshLibraryModels();
void slotCreatePlaylist();
void slotCreateCrate();
Expand Down
Loading