Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,8 @@ if(QT6)
src/qml/qmllibrarytracklistmodel.cpp
src/qml/qmlplayermanagerproxy.cpp
src/qml/qmlplayerproxy.cpp
src/qml/qmltablefromlistmodel.cpp
src/qml/qmltablefromlistmodelcolumn.cpp
src/qml/qmlvisibleeffectsmodel.cpp
src/qml/qmlwaveformoverview.cpp
)
Expand Down
330 changes: 253 additions & 77 deletions res/qml/Library.qml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Mixxx 1.0 as Mixxx
import QtQuick 2.12
import Qt.labs.qmlmodels
import QtQuick
import QtQuick.Controls 2.15
import "Theme"

Item {
Expand All @@ -11,129 +13,303 @@ Item {
id: libraryControl

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

ListView {
id: listView
HorizontalHeaderView {
id: horizontalHeader

function moveSelectionVertical(value) {
if (value == 0)
return ;
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: 5
syncView: tableView
model: ["Color", "Cover", "Artist", "Album", "Year", "Bpm", "Key", "Filetype", "Bitrate"]

const rowCount = model.rowCount();
if (rowCount == 0)
return ;
delegate: Item {
id: headerDlgt

required property int column
required property string modelData

implicitHeight: columnName.contentHeight + 5
implicitWidth: columnName.contentWidth + 5

BorderImage {
anchors.fill: parent
horizontalTileMode: BorderImage.Stretch
verticalTileMode: BorderImage.Stretch
source: Theme.imgPopupBackground

border {
top: 10
left: 20
right: 20
bottom: 10
}
}

Text {
id: columnName

currentIndex = Mixxx.MathUtils.positiveModulo(currentIndex + value, rowCount);
text: headerDlgt.modelData
anchors.fill: parent
anchors.margins: 5
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.family: Theme.fontFamily
font.capitalization: Font.AllUppercase
font.bold: true
font.pixelSize: Theme.buttonFontPixelSize
color: Theme.buttonNormalColor
}

Text {
id: sortIndicator

anchors.fill: parent
anchors.margins: 5
elide: Text.ElideRight
horizontalAlignment: Text.AlignRight
verticalAlignment: Text.AlignVCenter
font.family: Theme.fontFamily
font.capitalization: Font.AllUppercase
font.bold: true
font.pixelSize: Theme.buttonFontPixelSize
color: Theme.buttonNormalColor
}
}
}

TableView {
id: tableView

function loadSelectedTrackIntoNextAvailableDeck(play) {
const url = model.get(currentIndex).fileUrl;
if (!url)
const urls = this.selectionModel.selectedTrackUrls();
if (urls.length == 0)
return ;

Mixxx.PlayerManager.loadLocationUrlIntoNextAvailableDeck(url, play);
Mixxx.PlayerManager.loadLocationUrlIntoNextAvailableDeck(urls[0], play);
}

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

const player = Mixxx.PlayerManager.getPlayer(group);
if (!player)
const urls = this.selectionModel.selectedTrackUrls();
if (urls.length == 0)
return ;

player.loadTrackFromLocationUrl(url, play);
player.loadTrackFromLocationUrl(urls[0], play);
}

anchors.fill: parent
anchors.margins: 10
anchors.top: horizontalHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 5
clip: true
keyNavigationWraps: true
highlightMoveDuration: 250
highlightResizeDuration: 50
model: Mixxx.Library.model
Keys.onPressed: (event) => {
switch (event.key) {
case Qt.Key_Enter:
case Qt.Key_Return:
listView.loadSelectedTrackIntoNextAvailableDeck(false);
break;
focus: true
reuseItems: false
Keys.onUpPressed: this.selectionModel.moveSelectionVertical(-1)
Keys.onDownPressed: this.selectionModel.moveSelectionVertical(1)
Keys.onEnterPressed: this.loadSelectedTrackIntoNextAvailableDeck(false)
Keys.onReturnPressed: this.loadSelectedTrackIntoNextAvailableDeck(false)
columnWidthProvider: function(column) {
switch (column) {
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.

Is this related to the model defined in line 41? Could we make it so the custom default width is defined in model instead of here? Perhaps moving the model to be global property of library?

case 0:
return 30;
case 1:
return 50;
case 2:
case 3:
case 4:
return 300;
default:
return 100;
}
}

delegate: Item {
id: itemDlgt
model: Mixxx.TableFromListModel {
sourceModel: Mixxx.Library.model

Mixxx.TableFromListModelColumn {
decoration: "color"
edit: "fileUrl"
}
Comment on lines +148 to +151
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.

Could a Repeater be used here with the model defined at line 41?


required property int index
required property url fileUrl
required property string artist
required property string title
Mixxx.TableFromListModelColumn {
display: "coverArtUrl"
decoration: "coverArtColor"
edit: "fileUrl"
}

implicitWidth: listView.width
implicitHeight: 30
Mixxx.TableFromListModelColumn {
display: "artist"
edit: "fileUrl"
}

Text {
anchors.verticalCenter: parent.verticalCenter
text: itemDlgt.artist + " - " + itemDlgt.title
color: (listView.currentIndex == itemDlgt.index && listView.activeFocus) ? Theme.blue : Theme.deckTextColor
Mixxx.TableFromListModelColumn {
display: "album"
edit: "fileUrl"
}

Behavior on color {
ColorAnimation {
duration: listView.highlightMoveDuration
}
Mixxx.TableFromListModelColumn {
display: "year"
edit: "fileUrl"
}

Mixxx.TableFromListModelColumn {
display: "bpm"
edit: "fileUrl"
}

Mixxx.TableFromListModelColumn {
display: "key"
edit: "fileUrl"
}

Mixxx.TableFromListModelColumn {
display: "fileType"
edit: "fileUrl"
}

Mixxx.TableFromListModelColumn {
display: "bitrate"
edit: "fileUrl"
}
}

selectionModel: ItemSelectionModel {
function selectRow(row) {
const rowCount = this.model.rowCount();
if (rowCount == 0) {
this.clear();
return ;
}
const newRow = Mixxx.MathUtils.positiveModulo(row, rowCount);
this.select(this.model.index(newRow, 0), ItemSelectionModel.Rows | ItemSelectionModel.Select | ItemSelectionModel.Clear | ItemSelectionModel.Current);
}

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

const selected = this.selectedIndexes;
const oldRow = (selected.length == 0) ? 0 : selected[0].row;
this.selectRow(oldRow + value);
}

function selectedTrackUrls() {
return this.selectedIndexes.map((index) => {
return this.model.sourceModel.get(index.row).fileUrl;
});
}

Image {
id: dragItem
model: tableView.model
}

delegate: DelegateChooser {
DelegateChoice {
column: 0

Drag.active: dragArea.drag.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: {
"text/uri-list": itemDlgt.fileUrl,
"text/plain": itemDlgt.fileUrl
Rectangle {
id: trackColorDelegate

required property bool selected
required property color decoration

implicitWidth: 30
implicitHeight: 30
color: trackColorDelegate.decoration
}
anchors.fill: parent
}

MouseArea {
id: dragArea
DelegateChoice {
column: 1

anchors.fill: parent
drag.target: dragItem
onPressed: {
listView.forceActiveFocus();
listView.currentIndex = itemDlgt.index;
parent.grabToImage((result) => {
dragItem.Drag.imageSource = result.url;
});
Rectangle {
id: coverArtDelegate

required property color decoration
required property url display

implicitWidth: 60
implicitHeight: 30
color: coverArtDelegate.decoration

Image {
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: coverArtDelegate.display
clip: true
asynchronous: true
}
}
onDoubleClicked: listView.loadSelectedTrackIntoNextAvailableDeck(false)
}
}

highlight: Rectangle {
border.color: listView.activeFocus ? Theme.blue : Theme.deckTextColor
border.width: 1
color: "transparent"
DelegateChoice {
Item {
id: itemDelegate

required property int row
required property bool selected
required property string display

implicitWidth: 300
implicitHeight: 30

Text {
anchors.fill: parent
text: itemDelegate.display
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
color: itemDelegate.selected ? Theme.blue : Theme.white
}

Image {
id: dragItem

Drag.active: dragArea.drag.active
Drag.dragType: Drag.Automatic
Drag.supportedActions: Qt.CopyAction
Drag.mimeData: {
"text/uri-list": edit,
"text/plain": edit
}
anchors.fill: parent
}

MouseArea {
id: dragArea

anchors.fill: parent
drag.target: dragItem
onPressed: {
tableView.selectionModel.selectRow(itemDelegate.row);
parent.grabToImage((result) => {
dragItem.Drag.imageSource = result.url;
});
}
onDoubleClicked: {
tableView.selectionModel.selectRow(itemDelegate.row);
tableView.loadSelectedTrackIntoNextAvailableDeck(false);
}
}
}
}
}
}
}
Expand Down
Loading