I am currently working on implementing locally integrated menus (application menu in the titlebar) for Klassy decorations. One of the things that would be nice-to-have but isn’t required is the ability to support menu accelerators. I have the accelerator shortcut information (always Alt + a letter/number) from libdbusmenu, the hard part is implementing the shortcut in a nice way. I’ve tried:
Overriding the event method in the decoration to listen to all events the decoration is given. Unfortunately keyboard events are not passed in
Adding an event filter to QApplication
Looking around KDecoration3 & KWindowSystem to see if there is a method I’m missing.
Short of registering 36 Alt + letter/number global shortcuts (which would have a variety of issues; so I’d leave the feature out for the time being), is there a way to implement this that I missed?
If not, does this sound like an API that could/would be added/accepted (potentially as a shortcut property on DecorationButton)? Window decorations are already in a pretty privileged position (I know they can crash KWin; & if they can do that then they could use memory corruption to do almost anything), so I don’t think it’d be a security issue.
You should be able to add an event listener on the application window so your menu can intercept the “Alt” events it supports, that’s how Alt+F3 works for instance, and it supports accelerators, once opened.
And it should refresh itself at least with QMenu or trigger its actions. I am not sure this is the way.
You should probably inspire from the globally integrated menu, appmenu code but it does not seem to handle the accelerators either.
How can I get the application window from a decoration? I don’t see a way to get KWin’s underlying window from within the KDecoration API (the closest thing I can find that I have access to is KDecoration3::DecoratedWindow; I tested adding an event filter to it & that didn’t work).
Realized I could approach the problem in a different way by finding the decoration attached to a window & sending the events to it. While I couldn’t find a way to add an application/window-specific listener, while exploring I did find a general input filter for KWin. A minimal example of what I used to test the feasibility of such a method (for anyone who runs into this thread in the future):
#include <kwin/input.h>
#include <kwin/keyboard_input.h>
#include <kwin/window.h>
#include <kwin/workspace.h>
class InputTest : public KWin::InputEventFilter
{
public:
InputTest()
: KWin::InputEventFilter(KWin::InputFilterOrder::WindowAction)
{
}
bool keyboardKey(KWin::KeyboardKeyEvent *event) override
{
// NOTE: This receives a press & an unpress event; the accelerator would trigger on the press
qWarning() << "Key " << event->key << " Modifiers: " << event->modifiers;
if (auto window = KWin::Workspace::self()->activeWindow()) {
if (auto rawDecoration = window->decoration()) {
// MyDecoration here is a placeholder for the example
if (auto decoration = qobject_cast<MyDecoration *>(rawDecoration)) {
qWarning() << "Could call a method on the decoration to see if there is menu to open for this event!";
}
}
}
return KWin::InputEventFilter::keyboardKey(event);
}
static void register()
{
// Would have to ensure this runs once & doesn't register multiple
KWin::input()->installInputEventFilter(new InputTest());
}
};
InputFilter are integrated in KWin.
An new API for this might be necessary so KDecoration/KWin can relay the accelerator events.
KWin probably does not want to do the menu logic itself or at least in its thread.