Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow assigning more than one Hotcue to a Color in a Colorpalette #2902

Merged

Conversation

Swiftb0y
Copy link
Member

See Zulip Topic for Context

The current commit is broken and I'm only pushing it like this because I need some help implementing it.
I had implemented an approach previously that just parses the comma separated list using a regex using
a simple pure function but that led to a bunch of duplicated code later. So I instead decided to subclass
QStandardItem and parse the data in there. However, this is where I kinda got stuck. First of all because
I was unable to find answers to some questions in the Qt docs and second of all because I'm not familiar
enough with C++ to know whether my solution could even work.

Copy link
Member Author

@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.

These were some of the questions that came up and where I'm now stuck. I'd really appreciate some guidance here.

@@ -46,3 +49,27 @@ class ColorPaletteEditorModel : public QStandardItemModel {
bool m_bEmpty;
bool m_bDirty;
};

class HotcueIndexListItem : public QStandardItem {
Copy link
Member Author

Choose a reason for hiding this comment

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

The entire purpose of creating this class is to shift the "(de-)serialization" logic out of the model into the item so the item is responsible for translating between the String input and the data actually supposed to be represented as the string.


HotcueIndexListItem(const QList<int>& hotcueList = {});

void setData(const QVariant& value, int role = Qt::UserRole + 1) override;
Copy link
Member Author

Choose a reason for hiding this comment

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

I still don't quite understand the purpose of roles. I get that setData and data are "generic" accessors to get data for different purposes (such as displaying the data itself, providing a tooltip description etc). Since I only intent to intercept the data for editing and displaying, I should be able to reuse those roles instead of making my own role, right? Or what exactly am I supposed to do with the custom role.

Copy link
Member

Choose a reason for hiding this comment

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

Right.

HotcueIndexListItem::HotcueIndexListItem(const QList<int>& hotcueList = {})
: QStandardItem(), m_hotcueIndexList(hotcueList) {
}
QVariant HotcueIndexListItem::data(int role = Qt::UserRole + 1) const {
Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding here is that this acts as a getter for certain roles, right? Can't I just reuse the displayrole here and call the parent for every other role?

Copy link
Member

Choose a reason for hiding this comment

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

class YearItem: public QStandardItem {

I think you want the display role like here.

// m_hotcueIndexList.append(hotcueIndex);
// }
// }
const auto hotcueIndexStrings = value.toStringList();
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm just using a stringlist here because I wanted to test whether it was possible to somehow use that. Though I simply can't know since I don't fully understand how QVariant is supposed to work with a stringlist in this context. The Qt docs don't mention that.

@Swiftb0y Swiftb0y force-pushed the enable-multple-cues-per-color-autoassign branch from c273c3f to 0861564 Compare June 26, 2020 17:50
Copy link
Member Author

@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.

Cleaned up the code a bit. It still doesn't build because I'm unable to handle some stuff conceptually (see comments). I'm still not sure whether subclassing QStandardItem to parse and store a parsed version of the data for later access is a good idea. Could I get some feedback on that approach?

src/preferences/colorpaletteeditormodel.h Show resolved Hide resolved
src/preferences/colorpaletteeditormodel.cpp Show resolved Hide resolved
@@ -47,25 +56,39 @@ bool ColorPaletteEditorModel::dropMimeData(const QMimeData* data, Qt::DropAction

bool ColorPaletteEditorModel::setData(const QModelIndex& modelIndex, const QVariant& value, int role) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm still confused when setData on the model and when setData on the Item is called...

@@ -8,6 +15,8 @@ QIcon toQIcon(const QColor& color) {
return QIcon(pixmap);
}

const QRegularExpression hotcueListMatchingRegex(QStringLiteral("([0-9]+)[ ,]+"));
Copy link
Member Author

Choose a reason for hiding this comment

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

I tried making this part of the class but that didn't work. Is it fine to just leave it here?

src/preferences/colorpaletteeditormodel.h Show resolved Hide resolved
@@ -46,3 +49,25 @@ class ColorPaletteEditorModel : public QStandardItemModel {
bool m_bEmpty;
bool m_bDirty;
};

class HotcueIndexListItem : public QStandardItem {
Q_OBJECT
Copy link
Member

Choose a reason for hiding this comment

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

Remove this macro and it will build.

Copy link
Member Author

Choose a reason for hiding this comment

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

It currently doesn't build because of a bunch of syntax and typeerrors. I don't see how removing it would suddenly fix the build. Can I still remove the Q_OBJECT though? It doesn't use any signals/slots or moc specific mechisms, though the parent QStandardItem does.

Copy link
Member

Choose a reason for hiding this comment

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

Q_OBJECT macro defines some things in objects Inherited from QObject. QStandardItem does not inherit from QObject.

return QStandardItem::UserType;
};

QList<int> getHotcueIndexList() const {
Copy link
Member

Choose a reason for hiding this comment

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

Here you can return a const &

QList<mixxx::RgbColor> colors;
QMap<int, int> hotcueColorIndices;
for (int i = 0; i < rowCount(); i++) {
QStandardItem* pColorItem = item(i, 0);
QStandardItem* pHotcueIndexItem = item(i, 1);
mixxx::RgbColor::optional_t color = mixxx::RgbColor::fromQString(pColorItem->text());
auto pHotcueIndexItem = static_cast<HotcueIndexListItem*>(item(i, 1));
Copy link
Member

Choose a reason for hiding this comment

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

This is the place where you may check (ASSERT) the type()

@Swiftb0y
Copy link
Member Author

Swiftb0y commented Jul 6, 2020

Thanks for your reviews so far @daschuer. Unfortunately, I'm still a bit split on whether subclassing QStandardItem is actually the right approach here: When data is entered it passes through ColorPaletteEditorModel::setData which is then calling QStandardItem::setData further down the "chain". Subclassing QStandardItem to implement the parsing step is too late since we already need to have the field parsed in ColorPaletteEditorModel::setData however, once that data was parsed and validated, we are saving it again as a string in the QStandardItem. That would mean that each time the data is changed, we would evaluate x regexp (x being the number of colours in the palette) just to discard that parsed data afterward again.
The only two alternative approach I can think of would be either getting rid of the subclassed QStandardItem completely (meaning we'd have to parse each field each time just to throw away the result once we finished validating it), or just keeping it for storing the parsed hotcuelist and making the parsing step a static method. However, the second method isn't pretty either because we pretty much abuse the class just for storing the parsed data grouping functions.
What do you think?

@github-actions
Copy link

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 Oct 19, 2020
@Be-ing Be-ing changed the base branch from master to main October 23, 2020 23:16
Make it possible to assign the same color to different hotcues
by writing them as a comma-separated list into the table cell
@Swiftb0y Swiftb0y force-pushed the enable-multple-cues-per-color-autoassign branch from 0861564 to 8ffb21e Compare April 3, 2021 14:34
@github-actions github-actions bot added the ui label Apr 3, 2021
@Swiftb0y Swiftb0y added minor bug and removed stale Stale issues that haven't been updated for a long time. labels Apr 3, 2021
@Swiftb0y Swiftb0y marked this pull request as ready for review April 3, 2021 14:36
@Swiftb0y Swiftb0y requested a review from daschuer April 3, 2021 14:36
@Swiftb0y Swiftb0y changed the base branch from main to 2.3 April 3, 2021 14:36
@Swiftb0y
Copy link
Member Author

Swiftb0y commented Apr 3, 2021

I'd like to propose to add this to 2.3 as it fixes a missing feature / bug highlighted by #3746

Comment on lines 182 to 184
// If we have a non consequitive list of hotcue indexes, indexes are shifted down
// due to the sorting nature of QMap. This is intended, this way we have a color for every hotcue.
return ColorPalette(name, colors, hotcueColorIndices.values());
Copy link
Member Author

@Swiftb0y Swiftb0y Apr 3, 2021

Choose a reason for hiding this comment

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

I fear handling this silently results in confusion for the user. While we unfortunately don't have a "default color" we could insert into these empty positions, we should still test whether this shifting will occur and warn the user in that case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Opinions? @Holzhaus

Copy link
Member

Choose a reason for hiding this comment

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

If you want to implement a warning icon/message that is displayed in the edit dialog, go ahead.

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

How is this supposed to work? I typed2,3, 2, 3 and 2-3 into a lineedit field, and none of them works (field became empty after pressing enter or clicking elsewhere).

@Holzhaus
Copy link
Member

Holzhaus commented Apr 4, 2021

How is this supposed to work? I typed 2,3, 2, 3 and 2-3 into a lineedit field, and none of them works (field became empty after pressing enter or clicking elsewhere).

Ah ok, I didn't merge this correctly. It works, but there are some issues:

  • After closing the edit window, the indices becomes zero-based (1 becomes 0, 2 becomes 1, etc.)
  • It would be nice to allow ranges 1-3 instead of comma-separated values, too
  • Editing is a bit clunky become it's not possible to reassign hotcues without unassigning first. E.g. if I have some color that is assigned to 1 (or some combination like 1, 3), then I can't assign 1 to another color. I'd expect that to work and automatically unassign 1 on the other color.

@Swiftb0y
Copy link
Member Author

Swiftb0y commented Apr 5, 2021

After closing the edit window, the indices becomes zero-based (1 becomes 0, 2 becomes 1, etc.)

That is indeed a bug I overlooked, thanks for finding it.

It would be nice to allow ranges 1-3 instead of comma-separated values, too

Indeed, though its a bit more difficult to implement. I'll investigate.

Editing is a bit clunky become it's not possible to reassign hotcues without unassigning first. E.g. if I have some color that is assigned to 1 (or some combination like 1, 3), then I can't assign 1 to another color. I'd expect that to work and automatically unassign 1 on the other color.

I agree, though that increases complexity quite a bit, I'll see if I can refactor the code and work this in afterwards.

The entire feature hacks around some of Qts Model/View idioms, so either its not suited for what I'm trying to do or I don't understand Qt well enough to implement it properly. If anyone has ideas how to implement this better, please share.

@Swiftb0y Swiftb0y marked this pull request as draft April 7, 2021 10:21
Allow hotcue index assignments to consist of a mix of regular
indicies and ranges of indicies. For example "1 - 3, 5" to
express the list `[1, 2, 3, 5]`.
@Swiftb0y
Copy link
Member Author

Swiftb0y commented Apr 8, 2021

@Holzhaus

I implemented both of your suggestions. What do you think?

@Swiftb0y Swiftb0y force-pushed the enable-multple-cues-per-color-autoassign branch from 8d865d4 to d7d7ef0 Compare April 8, 2021 17:34
@Swiftb0y
Copy link
Member Author

I would love for this to go into 2.3, but as I said, I'd like to polish it more, but I don't know whether I'll find time for that. I guess I should just focus creating unit tests and any other polishing is optional.

@Holzhaus
Copy link
Member

I suggest we merge now and polish later, if you find the time.

@daschuer
Copy link
Member

The list af ideas to make it more intuitive, was just a brainstorming of alternative. Any improvement is welcome.

If a test looks ugly it is ok. Just a ugly test is better then no test.

I don't think it is worth the effort to refactor the functions out. That can be done if it is needed.

If you think you are ready here, just remove the draft state and we can merge.

@Swiftb0y
Copy link
Member Author

Thanks, I'll create some unit tests and then we can merge. If I won't find time today, I'll try to next week.

@daschuer
Copy link
Member

Thank you.

@Swiftb0y Swiftb0y marked this pull request as ready for review April 15, 2021 20:22
@Swiftb0y
Copy link
Member Author

I moved the free functions and constants into their own file and wrote some unit tests. What do you think?

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

Thank you, works well!

src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
src/test/rangelist_test.cpp Outdated Show resolved Hide resolved
src/util/rangelist.h Outdated Show resolved Hide resolved
return QStandardItem::UserType;
};

const QList<int>& getHotcueIndexList() const {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const QList<int>& getHotcueIndexList() const {
const QList<int>& getHotcueIndexList() const {

QList is implicitly shared, should we really return a const reference here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question. My guess was that explicitly constructing a QList with that reference at the call site when needed should still be pretty fast because of implicit sharing.

Copy link
Member

Choose a reason for hiding this comment

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

@daschuer @uklotzde opinions? I don't think really care and it can be changed in a subsequent PR you think it's needed. So no need to delay this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, references should be always const, except in a locals scope like fool[i].bar() and similar.

src/preferences/colorpaletteeditormodel.cpp Outdated Show resolved Hide resolved
@Swiftb0y
Copy link
Member Author

@Holzhaus thanks for the review. I addressed all but one of your suggestions.

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

Thank you, LGTM.

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

Ah, I just noticed we lost the mixxx namespace for rangelist.

src/util/rangelist.h Show resolved Hide resolved
src/util/rangelist.cpp Show resolved Hide resolved
@Swiftb0y Swiftb0y force-pushed the enable-multple-cues-per-color-autoassign branch from a1cf8ad to cd95da5 Compare April 19, 2021 11:17
@Swiftb0y
Copy link
Member Author

Thanks, I went back changed that commit and rebased the rest. Should be fine now.

Copy link
Member

@Holzhaus Holzhaus left a comment

Choose a reason for hiding this comment

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

LGTM, thanks.

@Holzhaus Holzhaus merged commit ff4275e into mixxxdj:2.3 Apr 19, 2021
Swiftb0y added a commit to Swiftb0y/manual that referenced this pull request Apr 19, 2021
@Swiftb0y Swiftb0y deleted the enable-multple-cues-per-color-autoassign branch April 19, 2021 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants