-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Keyboard: automatically reload mapping file and update tooltips #13082
base: main
Are you sure you want to change the base?
Conversation
connect(&m_fileWatcher, | ||
&QFileSystemWatcher::fileChanged, | ||
this, | ||
&KeyboardEventFilter::reloadKeyboardConfig); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a note that out of the box, the watcher may have a double reload due to the policy used by certain IDE edit files while ensuring resiliency to I/O error (Qt doc about that). It might be worth doing something similar to QML. Perhaps this might be worth introducing a util class to file watcher efficiently?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for this hint!
Do you mind filing a bug report so we can track that?
For now I'd stick with the implementation we also use for controller scripts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!
Note that I fixed it in controllers in my PR for screen controller
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to AutoFileReloader
now that #13607 has been merged.
oh, need to consider skin reload... edit: Done. |
c41be0a
to
6896dc8
Compare
6896dc8
to
428caf9
Compare
Done. |
428caf9
to
7f055f3
Compare
We could add dummy shortcuts (empty) to all currently unmapped menubar actions so users can add shortcuts themselves. Nothing that holds up this PR though. |
7f055f3
to
4583b2d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too important, but the upper/lowercasing of KeyboardEventFIlter
got a bit mixed up the commit message Keyboard: handle mapping file and config setting in KeyboardEventFIlter
`.
src/widget/wmainmenubar.cpp
Outdated
@@ -162,9 +169,10 @@ void WMainMenuBar::initialize() { | |||
QString createPlaylistText = tr("Create a new playlist"); | |||
auto* pLibraryCreatePlaylist = new QAction(createPlaylistTitle, this); | |||
pLibraryCreatePlaylist->setShortcut( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be cleaner to do the following?
m_pKbd->registerMenuBarActionForShortcut(
pLibraryRescan,
ConfigKey("[KeyboardShortcuts]", "LibraryMenu_Rescan"),
tr("Ctrl+Shift+L"));
instead of:
pLibraryRescan->setShortcut(
QKeySequence(m_pKbd->registerMenuBarActionGetKeySeqString(
pLibraryRescan,
ConfigKey("[KeyboardShortcuts]", "LibraryMenu_Rescan"),
tr("Ctrl+Shift+L"))));
This avoids having the setShortcut
both here (for the initial setup) and in KeyboardEventFilter
(for the reload), which feels a bit like code duplication. It also avoids repetition of pLibraryRescan
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, done.
This one has a conflict now. Can we remove the 2.5-beta milestone? |
Sure, assigned it to 2.5 for now because I think this is a bugfix, see #9814 Set to draft because I've just rebased this and am about to tweak it considering @cr7pt0gr4ph7's suggestions. |
4583b2d
to
61db431
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small nitpick: Spelling of "chnanged" in the commit message. LGTM otherwise.
61db431
to
0b63ef6
Compare
Thanks for taking the time to review!
Yeah, that happens when I work in bright sunlight... |
…gled or mapping file changed
…are toggled or mapping file changed
…are toggled or mapping file changed
0d83677
to
44f47e9
Compare
Oh sorry, I did a rebase instead of merging main. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
couple codestyle complaints
src/skin/legacy/legacyskinparser.cpp
Outdated
const ConfigKey configKey = ConfigKey::parseCommaSeparated(key); | ||
shortcutKeys.append(std::make_pair(configKey, QString())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
take advantage of rvalue move semantics.
const ConfigKey configKey = ConfigKey::parseCommaSeparated(key); | |
shortcutKeys.append(std::make_pair(configKey, QString())); | |
shortcutKeys.append({ConfigKey::parseCommaSeparated(key), QString()}); |
src/skin/legacy/legacyskinparser.h
Outdated
@@ -140,7 +140,8 @@ class LegacySkinParser : public QObject, public SkinParser { | |||
void setupWidget(const QDomNode& node, QWidget* pWidget, | |||
bool setupPosition=true); | |||
void setupConnections(const QDomNode& node, WBaseWidget* pWidget); | |||
void addShortcutToToolTip(WBaseWidget* pWidget, const QString& shortcut, const QString& cmd); | |||
const QString buildShortcutString(const QString& shortcut, const QString& cmd) const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const QString buildShortcutString(const QString& shortcut, const QString& cmd) const; | |
QString buildShortcutString(const QString& shortcut, const QString& cmd) const; |
src/coreservices.h
Outdated
std::shared_ptr<ConfigObject<ConfigValueKbd>> getKeyboardConfig() const { | ||
return m_pKbdConfig; | ||
} | ||
std::shared_ptr<ConfigObject<ConfigValueKbd>> getKeyboardConfig() const; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not in the header? Its body is small enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In KeyboardEventFilter yes.
Here it was small before, when CoreServices created the ConfigValueKbd.
Now KeyboardEventFilter creates it and we may not have created that yet, so I think I'll add another test, so it'll be slightly larger. Like this
std::shared_ptr<ConfigObject<ConfigValueKbd>> getKeyboardConfig() const {
VERIFY_OR_DEBUG_ASSERT(m_pKeyboardEventFilter) {
return std::make_shared<ConfigObject<ConfigValueKbd>>();
}
return m_pKeyboardEventFilter->getKeyboardConfig();
}
Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense. Though you may want to consider returning a nullptr instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, actually we don't need that anymore.
The only place where keys are requested is
mixxx/src/library/autodj/autodjfeature.cpp
Line 180 in 34dde6f
keyboard->getKeyboardConfig()->getValueString(ConfigKey("[AutoDJ]", "enabled")), |
and that gets a KeyboardEventFilter pointer from Library which has a CoreServices pointer.
And KeyboardEventFilter is initialized in the CoreServices ctor so this seems safe.
Will remove.
src/widget/wbasewidget.h
Outdated
m_shortcutKeys = cfgKeys; | ||
} | ||
|
||
const QList<std::pair<ConfigKey, QString>> getShortcutKeys() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const QList<std::pair<ConfigKey, QString>> getShortcutKeys() { | |
const QList<std::pair<ConfigKey, QString>>& getShortcutKeys() { |
src/widget/wbasewidget.h
Outdated
QString m_baseTooltip; | ||
QString m_shortcutTooltip; | ||
QString m_baseTooltipOptShortcuts; | ||
QList<std::pair<ConfigKey, QString>> m_shortcutKeys; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how big is this QList on average?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For knobs and sliders there'll be 5 4 controls (control, _up, _down, _up_small, _down_small).
For other widgets it's 1-2 (left- and right-click).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks. makes sense. This is not duplicated between widgets then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. It's set by LegacySkinParser and fetched by KeyboardEventFilter which looks up the keys per control, composes the tooltip and pushes that to the widget.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Btw this QList with a pair feels a bit clunky, so I switched to a QMap<ConfigKey, QString> (sorted by key) with convenient cons_key_value_iterator and structured binding.
This also has the benefit that shortcuts are now always sorted by control/sub-control's ConfigKey¹ (same group, so sorted by item) instead of order they've been added (QList) or sorted by the command's tr string (std::map<QString, ConfigKey>.
The slight performance regression shouldn't matter much as we add 4 items max.
¹WPushButton: _activate, _toggle
WKnob/WSlider: _down, _down_small, _up, _up_small
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm primarily concerned about the memory overhead from many large QLists in many widgets. But I can't make a good judgement call here either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ScopedTimer says: for 12 calls KeyboardEventFilter::updateWidgetShortcuts() takes around 0.08s on average.
The diff with/without the extra ControlPotmeter/WHotcueButton controls is hardly measurable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, but I was considering memory overhead (likely also not easily measurable because we're wasting so much already in other places), not runtime performance. Moreover 80ms is quite a lot...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moreover 80ms is quite a lot...
ah okay, without the qdebug lines it's down to ~4ms for ~1600 widgets (LateNight).
I'll push the latest version soonish.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...and now (without the debug stuff) it's obvious that the extra controls do have a noticeable impact:
with: 4-5ms
without: ~2.5ms
src/widget/wmainmenubar.cpp
Outdated
ConfigKey("[KeyboardShortcuts]", | ||
QString("OptionsMenu_EnableVinyl%1").arg(i + 1)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be nice if you could do this to the remaining lines if you're touching them anyways.
ConfigKey("[KeyboardShortcuts]", | |
QString("OptionsMenu_EnableVinyl%1").arg(i + 1)), | |
ConfigKey(QStringLiteral("[KeyboardShortcuts]"), | |
QStringLiteral("OptionsMenu_EnableVinyl%1").arg(i + 1)), |
src/widget/wmainmenubar.cpp
Outdated
pOptionsKeyboard->setCheckable(true); | ||
pOptionsKeyboard->setChecked(keyboardShortcutsEnabled); | ||
pOptionsKeyboard->setStatusTip(keyboardShortcutText); | ||
pOptionsKeyboard->setWhatsThis(buildWhatsThis(keyboardShortcutTitle, keyboardShortcutText)); | ||
connect(pOptionsKeyboard, &QAction::triggered, this, &WMainMenuBar::toggleKeyboardShortcuts); | ||
connect(pOptionsKeyboard, &QAction::triggered, m_pKeyboard, &KeyboardEventFilter::setEnabled); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
connect(pOptionsKeyboard, &QAction::triggered, m_pKeyboard, &KeyboardEventFilter::setEnabled); | |
connect(pOptionsKeyboard, &QAction::triggered, m_pKeyboard.get(), &KeyboardEventFilter::setEnabled); |
src/widget/wmainmenubar.cpp
Outdated
: QMenuBar(pParent), | ||
m_pConfig(pConfig), | ||
m_pKbdConfig(pKbdConfig) { | ||
m_pKeyboard(pKbd) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to avoid unnecessary ref count changes.
m_pKeyboard(pKbd) { | |
m_pKeyboard(std::move(pKbd)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is mostly copy pasted, right? Do you want to improve it or leave it?
some thoughts on the general tooltip update approach used here and performance: With the current implementatoin kbd mapppings are (re)loaded
so I think a tiny performance hit (GUI thread) in these non-critical situations is an acceptable tradeoff for having the GUI reflect the complete kbd mapping (all possible poti sub-controls) immediatly, even if it's just for double-checking that the mapping has been applied as expected. The current method is:
The downside: So I tried another approach, hoping that it would it be faster (in total, ie. load mapping file, check shortcuts, update widgets):
I had to implement the compare method and store more data in KeyboardEventFilter, and it turned out that this did not shorten the total time to the extent that it justifies the additional complexity (and potential errors/inconsistency), given that shortcuts are evaluated rarely. |
0a3d391
to
465ba9a
Compare
some thoughts on the general tooltip update approach used here and performance: With the current implementatoin kbd mapppings are (re)loaded
so I think a tiny performance hit (GUI thread) in these non-critical situations is an acceptable tradeoff for having the GUI reflect the complete kbd mapping (all possible poti sub-controls) immediatly, even if it's just for double-checking that the mapping has been applied as expected. The current method is:
The downside: So I tried another approach, hoping that it would it be faster (in total, ie. load mapping file, check shortcuts, update widgets):
I had to implement the compare method and store more data in KeyboardEventFilter, and it turned out that this did not shorten the total time to the extent that it justifies the additional complexity (and potential errors/inconsistency), given that shortcuts are evaluated rarely. |
IMO this is now ready for a final review. |
94f3037
to
e9f3ca0
Compare
e9f3ca0
to
c85011c
Compare
Yeeiij, finally!
Tooltips and the mapping are rebuilt completely if the mapping file has been changed (or removed).
As before, we look for the user's
Custom.kbd.cfg
first, then use the built-in mapping based on the selected locale.This 'only' covers the GUI mapping.Extending this to WMainMenuBar, as done in #1746, iscertainly possibly.done ✔️Inspired by #1746, but implemented more simply.
Replaces #13074
Fixes #9814