From 967dfa79563f5fa83aa27275bd57fc616a57ce55 Mon Sep 17 00:00:00 2001 From: Zaggy1024 Date: Mon, 24 Oct 2022 19:05:40 -0500 Subject: [PATCH] LibGUI: Handle Action keyboard shortcuts in Widget keydown Widgets can now prevent shortcut Actions from being called, which allows text input keydown handlers to override single key shortcuts. --- Userland/Libraries/LibGUI/Action.cpp | 16 +++++++ Userland/Libraries/LibGUI/Action.h | 1 + .../LibGUI/ConnectionToWindowServer.cpp | 10 ----- Userland/Libraries/LibGUI/Widget.cpp | 12 +++++ Userland/Libraries/LibGUI/Window.cpp | 44 +++++++++++++++++-- Userland/Libraries/LibGUI/Window.h | 2 + 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/Userland/Libraries/LibGUI/Action.cpp b/Userland/Libraries/LibGUI/Action.cpp index 1ac2417b75..5408c02ac0 100644 --- a/Userland/Libraries/LibGUI/Action.cpp +++ b/Userland/Libraries/LibGUI/Action.cpp @@ -125,6 +125,22 @@ Action::~Action() } } +void Action::process_event(Window& window, Event& event) +{ + if (is_enabled()) { + flash_menubar_menu(window); + activate(); + event.accept(); + return; + } + if (swallow_key_event_when_disabled()) { + event.accept(); + return; + } + + event.ignore(); +} + void Action::activate(Core::Object* activator) { if (!on_activation) diff --git a/Userland/Libraries/LibGUI/Action.h b/Userland/Libraries/LibGUI/Action.h index 87c5762b6b..efe0f1c5fe 100644 --- a/Userland/Libraries/LibGUI/Action.h +++ b/Userland/Libraries/LibGUI/Action.h @@ -97,6 +97,7 @@ public: Function on_activation; void activate(Core::Object* activator = nullptr); + void process_event(Window& window, Event& event); void flash_menubar_menu(GUI::Window& window); bool is_enabled() const { return m_enabled; } diff --git a/Userland/Libraries/LibGUI/ConnectionToWindowServer.cpp b/Userland/Libraries/LibGUI/ConnectionToWindowServer.cpp index 6a29e66a62..e9060080b9 100644 --- a/Userland/Libraries/LibGUI/ConnectionToWindowServer.cpp +++ b/Userland/Libraries/LibGUI/ConnectionToWindowServer.cpp @@ -188,16 +188,6 @@ void ConnectionToWindowServer::key_down(i32 window_id, u32 code_point, u32 key, auto key_event = make(Event::KeyDown, (KeyCode)key, modifiers, code_point, scancode); - if (auto* action = action_for_shortcut(*window, Shortcut(key_event->modifiers(), key_event->key()))) { - if (action->is_enabled()) { - action->flash_menubar_menu(*window); - action->activate(); - return; - } - if (action->swallow_key_event_when_disabled()) - return; - } - bool focused_widget_accepts_emoji_input = window->focused_widget() && window->focused_widget()->on_emoji_input; if (!window->blocks_emoji_input() && focused_widget_accepts_emoji_input && (modifiers == (Mod_Ctrl | Mod_Alt)) && key == Key_Space) { auto emoji_input_dialog = EmojiInputDialog::construct(window); diff --git a/Userland/Libraries/LibGUI/Widget.cpp b/Userland/Libraries/LibGUI/Widget.cpp index 1def39c505..b92e0e6a98 100644 --- a/Userland/Libraries/LibGUI/Widget.cpp +++ b/Userland/Libraries/LibGUI/Widget.cpp @@ -344,10 +344,22 @@ void Widget::event(Core::Event& event) void Widget::handle_keydown_event(KeyEvent& event) { keydown_event(event); + if (event.is_accepted()) + return; + + if (auto action = Action::find_action_for_shortcut(*this, Shortcut(event.modifiers(), event.key()))) { + action->process_event(*window(), event); + if (event.is_accepted()) + return; + } + if (event.key() == KeyCode::Key_Menu) { ContextMenuEvent c_event(window_relative_rect().bottom_right(), screen_relative_rect().bottom_right()); dispatch_event(c_event); + return; } + + event.ignore(); } void Widget::handle_paint_event(PaintEvent& event) diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp index 9d930f1cc2..417096cf12 100644 --- a/Userland/Libraries/LibGUI/Window.cpp +++ b/Userland/Libraries/LibGUI/Window.cpp @@ -454,6 +454,37 @@ void Window::handle_multi_paint_event(MultiPaintEvent& event) ConnectionToWindowServer::the().async_did_finish_painting(m_window_id, rects); } +void Window::propagate_shortcuts_up_to_application(KeyEvent& event, Widget* widget) +{ + VERIFY(event.type() == Event::KeyDown); + auto shortcut = Shortcut(event.modifiers(), event.key()); + Action* action = nullptr; + + if (widget) { + VERIFY(widget->window() == this); + + do { + action = widget->action_for_shortcut(shortcut); + if (action) + break; + + widget = widget->parent_widget(); + } while (widget); + } + + if (!action) + action = action_for_shortcut(shortcut); + if (!action) + action = Application::the()->action_for_shortcut(shortcut); + + if (action) { + action->process_event(*this, event); + return; + } + + event.ignore(); +} + void Window::handle_key_event(KeyEvent& event) { if (!m_focused_widget && event.type() == Event::KeyDown && event.key() == Key_Tab && !event.ctrl() && !event.alt() && !event.super()) { @@ -465,9 +496,16 @@ void Window::handle_key_event(KeyEvent& event) return default_return_key_widget()->dispatch_event(event, this); if (m_focused_widget) - return m_focused_widget->dispatch_event(event, this); - if (m_main_widget) - return m_main_widget->dispatch_event(event, this); + m_focused_widget->dispatch_event(event, this); + else if (m_main_widget) + m_main_widget->dispatch_event(event, this); + + if (event.is_accepted()) + return; + + // Only process shortcuts if this is a keydown event. + if (event.type() == Event::KeyDown) + propagate_shortcuts_up_to_application(event, nullptr); } void Window::handle_resize_event(ResizeEvent& event) diff --git a/Userland/Libraries/LibGUI/Window.h b/Userland/Libraries/LibGUI/Window.h index 68c4b372c8..55e60a26db 100644 --- a/Userland/Libraries/LibGUI/Window.h +++ b/Userland/Libraries/LibGUI/Window.h @@ -234,6 +234,8 @@ public: void set_always_on_top(bool always_on_top = true); + void propagate_shortcuts_up_to_application(KeyEvent& event, Widget* widget); + protected: Window(Core::Object* parent = nullptr); virtual void wm_event(WMEvent&);