Effect selector widget#1135
Conversation
|
I am thinking we should remove the effect full name versus short name distinction. I think we only need short names and long descriptions. |
355bbc6 to
2b05042
Compare
The issue now is that we have no rule, what is short and what is long. Can we come to a rule, that is usable for Translators and considered proportional fonts? |
|
|
||
| const QList<QString> EffectsManager::getAvailableEffects() const { | ||
| QList<QString> availableEffects; | ||
| bool alphabetizeEffectNameIdPairs(const QPair<QString, QString>& pair1, |
There was a problem hiding this comment.
Using QPair is an antipattern. Please create your own proper named struct or class.
There was a problem hiding this comment.
Even as a typedef: using NameIdPair = QPair<QString, QString>
There was a problem hiding this comment.
I think I'll refactor this so EffectManager keeps a QList<EffectManifest> sorted alphabetically. But yeah, I don't understand what the issue with QPair is.
There was a problem hiding this comment.
QPair is bad, because you cannot see what .first and .second is.
It requires explanation on every usage.
class NameIdPair {
public:
QString Id;
QString Name;
}
would be self explainig
There was a problem hiding this comment.
In this case you can just use a typedef though...
There was a problem hiding this comment.
You cannot get rid of the meaningless first and second members.
| QList<QString> availableEffects; | ||
| bool alphabetizeEffectNameIdPairs(const QPair<QString, QString>& pair1, | ||
| const QPair<QString, QString>& pair2) { | ||
| return pair1.second < pair2.second; |
There was a problem hiding this comment.
Use QString::localeAwareCompare()
| // Each returned QPair has the effect ID and full name. | ||
| const QList<QPair<QString, QString> > EffectsManager::getEffectNamesFiltered( | ||
| EffectManifestFilterFnc filter) const { | ||
| QList<QPair<QString, QString> > filteredEffectNames; |
| int index = effects.indexOf(effectId); | ||
| if (++index >= effects.size()) { | ||
| int index; | ||
| for (index = 0; index < idNamePairs.size(); ++index) { |
There was a problem hiding this comment.
This can become a range based loop
| } | ||
|
|
||
| QString EffectsManager::getPrevEffectId(const QString& effectId) { | ||
| const QList<QString> effects = getAvailableEffects(); |
There was a problem hiding this comment.
I think you can move out the common inner part of the Prev and Next functions.
There was a problem hiding this comment.
That's so little code that I don't think it would be worth it.
|
I think the effect selector is a candidate for the long names by default. Is it possible to show the short names in the Box and the long names and short in the selector? A tool tip showing the description might be also usable. |
You are conflating effect names and parameter names. I think short names for parameters should stay, but we should move to one short name for effects.
That was what I intended to do initially. However, I realized it would be a bit confusing to see the text change when selecting a new effect versus seeing the selected effect name. "Short (Long Name)" would be overcomplicated and unnecessarily long. The default behavior of QComboBox is to expand to the width of the longest item in the list. I think this behavior makes sense and should be kept, so the list items should be kept short. Thus the rule for translators would be, "translate this to as short of a string as possible that is still understandable".
That is already implemented in this PR, which is why I think long names are unnecessary. |
Actually, the same applies to effect parameters. Space will always be limited where they are displayed, so I don't think there is ever a need for an extended name. The description is sufficient. |
| addItem(effectPair.second, QVariant(effectPair.first)); | ||
| } | ||
| //: Displayed when no effect is loaded | ||
| addItem(tr("None"), QVariant(nullEffectId)); |
There was a problem hiding this comment.
Instead of having a null string constant, you can use the fact the QVariant and QString both have isNull methods for when their no-args constructor is used.
QString("").isNull() == false and QString().isNull() == true
| } | ||
| } | ||
|
|
||
| void WEffectSelector::slotEffectSelected(int newIndex) { |
There was a problem hiding this comment.
check for null m_pChainSlot early and exit?
| const QString id = itemData(newIndex).toString(); | ||
| EffectPointer pEffect; | ||
|
|
||
| bool loadNew = false; |
There was a problem hiding this comment.
I think a widget shouldn't have the instantiate/replace logic in it -- could you add a EffectRack::loadEffect(chainslotnumber, effectslotnumber, effectid) (alongside loadNextEffect/loadPrevEffect) method, then call it here?
| WBaseWidget(pParent), | ||
| m_pEffectsManager(pEffectsManager) { | ||
|
|
||
| setInsertPolicy(QComboBox::InsertAlphabetically); |
There was a problem hiding this comment.
It turns out this only applies to user-entered data, not programmatically entered data -- so you can remove it.
|
moved |
|
Ups, I commented the wrong PR, moving my comment. |
and remove use of QPair
get rid of QPairs
234ded1 to
8d6bd7b
Compare
|
Now that I have gone through the trouble of removing the QPairs, I understand why it is not a good idea to use them. |
rryan
left a comment
There was a problem hiding this comment.
Looks great! LGTM other than a couple nits.
| void EffectsManager::slotBackendRegisteredEffect(EffectManifest manifest) { | ||
| m_availableEffectManifests.append(manifest); | ||
| qSort(m_availableEffectManifests.begin(), m_availableEffectManifests.end(), | ||
| alphabetizeEffectManifests); |
There was a problem hiding this comment.
To avoid having to re-sort the list you can find the right spot to insert with qLowerBound.
auto insertion_point = qLowerBound(m_availableEffectManifests.begin(), m_availableEffectManifests.end(), manifest, alphabetizeEffectManifests);
m_availableEffectManifests.insert(insertion_point, manifest);| for (const auto& manifest : availableEffectManifests) { | ||
| QListWidgetItem* pItem = new QListWidgetItem(); | ||
| pItem->setData(Qt::UserRole, manifest.id()); | ||
| // Use short names to match order and appearance in WEffectSelector |
There was a problem hiding this comment.
Since this shortName/name logic appears in 3 places (the sorting function, here, and WEffectSelector) should this be a method on the manifest that is called in these places?
| const char* kDefaultMasterEqId = "none"; | ||
| // This is necessary so effect selections set to None are saved | ||
| // and restored across restarts instead of reset to default. | ||
| const char* kNullEffectId = "none"; |
There was a problem hiding this comment.
Using null values for strings is kind of a Qt anti-pattern because Qt has a built-in null value for strings which is distinct from empty. I think you need this because ConfigObject::getValueString does not distinguish between null vs empty strings (and the existing code below uses it). ConfigObject::getValue (the replacement for getValueString) does distinguish between these cases (I added a test to confirm that empty entries survive across restarts here: 63a25e6)
I think instead of using this placeholder, you could use the new getValue method and everything should work. Empty strings in the user config will be left as no effect, while null entries will use the specified default.
|
@daschuer I think all of your concerns were addressed (?) so I'll merge this once CI is green. |
|
@Be-ing: Am manual test can't hurt. Do you have a testing skin? |
|
I'll update #1063 after this is merged. |
|
I have just tested it and it works nice. I have noticed three small issues:
|
Is this because there are not translated shortNames yet? shortNames should not need eliding. I'll look into the other issues. |
|
I think the box should elide in any case. This will be required for LV2 effects. |
|
Okay. When LV2 support is introduced we may consider adding shortNames for them. Middle eliding seems kinda odd. Shouldn't it elide at the end? |
|
Without eliding, the width of the widget automatically expands to the width of the widest item, at least in the context of replacing the |
|
We cannot add names for LV2 effects, because we do not know them before. This is my output of lv2ls -n: Calf Analyzer |
|
Hm, the widget shows the English shortNames for effects that have them when I run Mixxx with fr_FR locale. Does every effect need a shortName declared in its manifest to let translators provide shortNames even if a shortName is not needed for that particular effect's English name? |
| setItemData(i, QVariant(description), Qt::ToolTipRole); | ||
| // The <span/> is a hack to get Qt to treat the string as rich text so | ||
| // it automatically wraps long lines. | ||
| setItemData(i, QVariant("<span/>" + description), Qt::ToolTipRole); |
There was a problem hiding this comment.
This wraps at a rather narrow width, but I don't think there's a way to set Qt to wrap at a wider width. It's a little awkward, but it's better than having very wide tooltips.
There was a problem hiding this comment.
I tried using <div style=\"width: 450px;\"> + description + </div> but that did not change the wrapping width.
Good point! I would say yes. Did you consider to add a comment that gives a hint what short and long is? How about the proposed "XXXXX" measure? |
I'm not sure what hint we'd give. What length it would actually cut off depends on the font face, font size, and width allowed for the WEffectSelector by the skin.
What proposal are you referring to? |
|
If every effect will have a shortName, I think the new EffectManifest::displayName method can be removed and shortName can be used in its place. |
|
Something like this: "XXXXXXX" |
|
Let's continue to work on the effect name lengths and translation questions -- but let's not hold up this PR. |
|
Not off the top of my head, but take a look at the QComboBox styling documentation. |

Add a new QComboBox widget to select an effect. This has several advantages:
It is easy to replace EffectName elements in skin XML with the new EffectSelector element. All that is required is changing the element name. The skin will need to define styles for WEffectSelector to make the colors match the skin instead of the system Qt colors.