Keyboard shortcut/hotkey for actions within a dock area plugin

• Dec 29, 2020 - 23:24

I have a plugin with pluginType: "dock" that has some buttons in it's UI.

Is there any way to allow keyboard shortcuts for the button's actions?

I read bazhenoff's answer in https://musescore.org/en/node/314156

His suggestion is to have a focused key listener area in the plugin:

        Item {
            focus: true
            Keys.onPressed: {
                console.log("Key press in key area detected: " + event.key)
            }
        }

It's quite close to what I need but there is one problem: When I press the shortcut to open my plugin (as a shortcut leader key), it always starts a new instance of my plugin instead of focusing it. So I have no way to focus my plugin with a keyboard shortcut.

Is there a way/work around?


Comments

1) Yes, application-level shortcuts can be defined using Shortcut QML type with Qt.ApplicationShortcut scope:

Shortcut {
    sequence: "Ctrl+D"
    context: Qt.ApplicationShortcut
    onActivated: console.log("shortcut!");
}

The format which is accepted by sequence property is described, for example, here as well as in that property documentation.

2) Requesting keyboard focus for a plugin is not that straightforward but is doable. The issue is that a plugin is QML item while the entire application is QtWidgets-based, and in order to embed plugin view into the application the plugin is actually rendered in a separate window. Therefore in order to focus a plugin you should activate that window first. This can be done in QML using Window type.

  • First, you have to find the window which your plugin is attached to with Window.window attached property:
import QtQuick.Window 2.2
 
MuseScore {
    id: plugin // assign ID to the plugin item to be able to refer to it from a shortcut
    pluginType: "dock"
    readonly property var window: Window.window
}
onActivated: plugin.window.requestActivate()
  • If you want to focus a specific item within your plugin window you should also call something like forceActiveFocus() on it:
onActivated: {
    plugin.window.requestActivate(); // activate the window first
    someItem.forceActiveFocus(); // focus an item with id "someItem" (assuming it exists)
}
  • Overall, everything said above combines into something like this:
import QtQuick 2.6
import QtQuick.Window 2.2
import MuseScore 3.0
 
MuseScore {
    id: plugin
    pluginType: "dock"
    readonly property var window: Window.window
 
    Item { id: someItem } // in a real plugin that would be TextArea, Button or some other control
 
    Shortcut {
        sequence: "Ctrl+D"
        context: Qt.ApplicationShortcut
        onActivated: {
            plugin.window.requestActivate();
            someItem.forceActiveFocus();
        }
    }
}

In reply to by dmitrio95

Thank you very much for this detailed answer!

The application shortcuts already work quite well for me.

Do you happen to know, how I can activate my existing plugin instead of starting a another instance? I guess onRun I could find the plugin window by id and if it exists foundWindow.requestActivate(); Qt.quit();?

In reply to by dmitrio95

Well, it turns out I was not fully correct. As all plugins are currently executed by the same instance of QML engine, it is possible to pass messages between them using QML singleton types (see https://doc.qt.io/qt-5/qtqml-modules-qmldir.html). I attach an example of such plugin to this comment: you can launch several instances of this plugin and see how changing text in one plugin instance changes the displayed text in all the launched plugin instances. Apparently this mechanism can be used to trigger focus request in the relevant plugin instance.

The issue is, however, that the new instance of such plugin would also have to somehow close itself. As it has been described in this comment, Qt.quit() isn't really a good option here since it would force all plugin instances to quit, and dock plugins do not seem to have an option to close only current instance of a plugin without closing all the other displayed plugin instances as well. So in this case the plugin would be able to focus the previous plugin instance but the new instance would still be launched and displayed.

Maybe the solution would be to add a dedicated quit() method to plugin API that would allow to close exactly one plugin instance (if we still have some time before 3.6 release). But other than that there doesn't seem to be a good way to get this working.

Attachment Size
singleton_test.zip 1.14 KB

Do you still have an unanswered question? Please log in first to post your question.