Skip to content

QML: Add library table#4594

Draft
Holzhaus wants to merge 7 commits into
mixxxdj:mainfrom
Holzhaus:qml-library-table
Draft

QML: Add library table#4594
Holzhaus wants to merge 7 commits into
mixxxdj:mainfrom
Holzhaus:qml-library-table

Conversation

@Holzhaus
Copy link
Copy Markdown
Member

@Holzhaus Holzhaus commented Dec 30, 2021

Based on #4567 and #4654.

@ywwg
Copy link
Copy Markdown
Member

ywwg commented Feb 3, 2022

similar to the waveform PR, I would like to keep QML moving quickly. If it's possible to get some experimental code merged in as a start, let's do it.

@Holzhaus Holzhaus force-pushed the qml-library-table branch 2 times, most recently from c30b370 to 83a2d0c Compare February 7, 2022 21:02
@Holzhaus Holzhaus changed the title QML: Add library table [WIP] QML: Add library table Feb 7, 2022
@Holzhaus
Copy link
Copy Markdown
Member Author

Holzhaus commented Feb 7, 2022

Ok, I think this is ready to review.

I'm particulary interested in feedback regard the custom TableFromListModel class, which remaps roles from a listmodel into a tablemodel (so that we can use it as model for a QML TableView). The implementation is based on what Qt's TableModel QML type does.

The advantage of this is that the skin designer can decide what rows should be displayed, what order they should have, etc. and does not have to make assumptions about what column of the table contains what. Our playlist is displayed as a table, but semantically it's just a list of tracks, not a table.

@Holzhaus Holzhaus marked this pull request as ready for review February 7, 2022 21:11
@Holzhaus Holzhaus marked this pull request as draft February 12, 2022 16:12
@Holzhaus
Copy link
Copy Markdown
Member Author

Rebased on #4673 to make stuff like SelectTrackKnob work.

@Holzhaus Holzhaus force-pushed the qml-library-table branch 2 times, most recently from a465bb4 to a0a0c6e Compare February 16, 2022 16:04
@Holzhaus Holzhaus marked this pull request as ready for review February 16, 2022 16:12
@Holzhaus
Copy link
Copy Markdown
Member Author

Rebased, ready to review again.

@Holzhaus Holzhaus requested a review from uklotzde February 16, 2022 16:13
Comment thread src/qml/qmltablefromlistmodelcolumn.cpp
Comment thread src/qml/qmltablefromlistmodelcolumn.h Outdated
Comment thread src/qml/qmltablefromlistmodelcolumn.cpp
\
void QmlTableFromListModelColumn::getterSetterName( \
const QJSValue& stringOrFunction) { \
if (!stringOrFunction.isString() && !stringOrFunction.isCallable()) { \
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.

Due to the macro nature all this code is duplicated. It would be better to use helper functions for this.
This also helps to get around of some macro weirdness when debugging.

Maybe there is a chance to omit using the macro entirely. Currently it has already 7 parameter.
Maybe we can replace it wit 7 lines of c++ code forwarding to a template or something.

If you wish to go with the macro solution you may consider to use only the property name as parameter.
Like shown here:

#define MIXXX_DECL_PROPERTY(TYPE, NAME, CAP_NAME) \

But I would prefer a c++ based solution even if it involves more writing initaly.

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.

This macro-based solution works fine IMHO. If you want to replace the macros in a follow-up PR, feel free to do so.

@uklotzde
Copy link
Copy Markdown
Contributor

@Holzhaus With a fresh profile the library panel remains empty. Warnings only affect other QML files.

@Holzhaus
Copy link
Copy Markdown
Member Author

Holzhaus commented Mar 7, 2022

@uklotzde

With a fresh profile the library panel remains empty.

What do you mean by "it remains empty"? Is this a regression from the current QML library?

@Holzhaus
Copy link
Copy Markdown
Member Author

Holzhaus commented Mar 10, 2022

@uklotzde

With a fresh profile the library panel remains empty.

What do you mean by "it remains empty"? Is this a regression from the current QML library?

Ah, this is unrelated to this PR and also happens on main (with QML). The reason for this is the weird way the model is populated. It requires calling TrackModel::select(), and apparently the legacy GUI code takes care of that....

A workaround for main could look like this:

diff --git a/res/qml/Library.qml b/res/qml/Library.qml
index 657cb58203..917e73147d 100644
--- a/res/qml/Library.qml
+++ b/res/qml/Library.qml
@@ -43,6 +43,7 @@ Item {
             }

             function loadSelectedTrackIntoNextAvailableDeck(play) {
+                model.select();
                 const url = model.get(currentIndex).fileUrl;
                 if (!url)
                     return ;
@@ -51,6 +52,7 @@ Item {
             }

             function loadSelectedTrack(group, play) {
+                model.select();
                 const url = model.get(currentIndex).fileUrl;
                 if (!url)
                     return ;
diff --git a/src/qml/qmllibrarytracklistmodel.h b/src/qml/qmllibrarytracklistmodel.h
index 490ff4508c..65b89cce46 100644
--- a/src/qml/qmllibrarytracklistmodel.h
+++ b/src/qml/qmllibrarytracklistmodel.h
@@ -1,4 +1,5 @@
 #pragma once
+#include "library/trackmodel.h"
 #include <QIdentityProxyModel>
 #include <QtQml>

@@ -25,6 +26,10 @@ class QmlLibraryTrackListModel : public QIdentityProxyModel {
     QmlLibraryTrackListModel(LibraryTableModel* pModel, QObject* pParent = nullptr);
     ~QmlLibraryTrackListModel() = default;

+    Q_INVOKABLE void select() {
+        dynamic_cast<TrackModel*>(sourceModel())->select();
+    }
+
     QVariant data(const QModelIndex& index, int role) const override;
     int columnCount(const QModelIndex& index = QModelIndex()) const override;
     QHash<int, QByteArray> roleNames() const override;

Now, if you double click a track in the table after dragging and dropping a new track onto a deck, the new track shows up. :-/

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 9, 2022

This PR is marked as stale because it has been open 90 days with no activity.

@github-actions github-actions Bot added the stale Stale issues that haven't been updated for a long time. label Jun 9, 2022
@Holzhaus Holzhaus force-pushed the qml-library-table branch 2 times, most recently from fa54d6e to 7f250a3 Compare April 7, 2023 21:00
@Holzhaus Holzhaus force-pushed the qml-library-table branch from 7f250a3 to 2280857 Compare April 7, 2023 21:14
@Holzhaus
Copy link
Copy Markdown
Member Author

Merge?

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

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

I'm still torn on the amount of boilerplate required for this. I'll take a closer look asap. Thank you for reviving this again.

Copy link
Copy Markdown
Member

@Swiftb0y Swiftb0y left a comment

Choose a reason for hiding this comment

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

don't think I can find the time for much more right now.

Comment on lines +16 to +20
Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL)
Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE
setSourceModel NOTIFY sourceModelChanged REQUIRED FINAL)
Q_PROPERTY(QQmlListProperty<mixxx::qml::QmlTableFromListModelColumn> columns
READ columns CONSTANT FINAL)
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.

have you experimented with QProperty, could it reduce some of the boilerplate?

Comment on lines +13 to +14

class QmlTableFromListModel : public QAbstractTableModel, public QQmlParserStatus {
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.

Can you add an explanation for why this class exists exactly?

@github-actions github-actions Bot removed the stale Stale issues that haven't been updated for a long time. label Aug 31, 2023
Comment thread res/qml/Library.qml
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?

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

Comment on lines +36 to +40
const QHash<int, QByteArray>& sourceRoleNames = m_pSourceModel->roleNames();
for (auto it = sourceRoleNames.cbegin(); it != sourceRoleNames.cend(); it++) {
int roleId = it.key();
m_sourceRoles.insert(QString::fromUtf8(it.value()), roleId);
}
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 the for-each loop format not possible here?

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.

I don't think so, we want both key and value.

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.

There is a new method asKeyValueRange in Qt 6.4 allowing this. Not sure if it is worth having behind preprocessor for when we upgrade to this Qt version?

Suggested change
const QHash<int, QByteArray>& sourceRoleNames = m_pSourceModel->roleNames();
for (auto it = sourceRoleNames.cbegin(); it != sourceRoleNames.cend(); it++) {
int roleId = it.key();
m_sourceRoles.insert(QString::fromUtf8(it.value()), roleId);
}
const QHash<int, QByteArray>& sourceRoleNames = m_pSourceModel->roleNames();
for (auto [roleId, value] : sourceRoleNames.asKeyValueRange()) {
m_sourceRoles.insert(QString::fromUtf8(value), roleId);
}

return m_getters;
}
const QHash<int, QString> QmlTableFromListModelColumn::supportedRoleNames() {
QHash<int, QString> names;
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.

Perhaps this could be a constant like kRoleNames?

QJSValue setterAtRole(const QString& roleName);
const QHash<QString, QJSValue> getters() const;
static const QHash<int, QString> supportedRoleNames();
Q_SIGNALS:
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.

perhaps using signals would be more consistent with the rest of the codebase

@acolombier
Copy link
Copy Markdown
Member

Marking this as draft for now @Holzhaus - feel free to mark it ready again once we should give it another go!

@acolombier acolombier marked this pull request as draft February 5, 2025 18:27
@acolombier acolombier added the stale Stale issues that haven't been updated for a long time. label Mar 24, 2025
@acolombier acolombier self-assigned this Jun 18, 2025
@github-actions github-actions Bot removed the stale Stale issues that haven't been updated for a long time. label Sep 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

6 participants