diff --git a/LibGUI/GAction.cpp b/LibGUI/GAction.cpp index c183340e0e..a402215a29 100644 --- a/LibGUI/GAction.cpp +++ b/LibGUI/GAction.cpp @@ -3,44 +3,55 @@ #include #include -GAction::GAction(const String& text, const String& custom_data, Function on_activation_callback) +GAction::GAction(const String& text, const String& custom_data, Function on_activation_callback, GWidget* widget) : on_activation(move(on_activation_callback)) , m_text(text) , m_custom_data(custom_data) + , m_widget(widget ? widget->make_weak_ptr() : nullptr) { } -GAction::GAction(const String& text, Function on_activation_callback) - : GAction(text, String(), move(on_activation_callback)) +GAction::GAction(const String& text, Function on_activation_callback, GWidget* widget) + : GAction(text, String(), move(on_activation_callback), widget) { } -GAction::GAction(const String& text, RetainPtr&& icon, Function on_activation_callback) +GAction::GAction(const String& text, RetainPtr&& icon, Function on_activation_callback, GWidget* widget) : on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) + , m_widget(widget ? widget->make_weak_ptr() : nullptr) { } -GAction::GAction(const String& text, const GShortcut& shortcut, Function on_activation_callback) - : GAction(text, shortcut, nullptr, move(on_activation_callback)) +GAction::GAction(const String& text, const GShortcut& shortcut, Function on_activation_callback, GWidget* widget) + : GAction(text, shortcut, nullptr, move(on_activation_callback), widget) { } -GAction::GAction(const String& text, const GShortcut& shortcut, RetainPtr&& icon, Function on_activation_callback) +GAction::GAction(const String& text, const GShortcut& shortcut, RetainPtr&& icon, Function on_activation_callback, GWidget* widget) : on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) , m_shortcut(shortcut) + , m_widget(widget ? widget->make_weak_ptr() : nullptr) { - GApplication::the().register_shortcut_action(Badge(), *this); + if (m_widget) { + m_scope = ShortcutScope::WidgetLocal; + m_widget->register_local_shortcut_action(Badge(), *this); + } else { + m_scope = ShortcutScope::ApplicationGlobal; + GApplication::the().register_global_shortcut_action(Badge(), *this); + } } GAction::~GAction() { - if (m_shortcut.is_valid()) - GApplication::the().unregister_shortcut_action(Badge(), *this); + if (m_shortcut.is_valid() && m_scope == ShortcutScope::ApplicationGlobal) + GApplication::the().unregister_global_shortcut_action(Badge(), *this); + if (m_widget && m_scope == ShortcutScope::WidgetLocal) + m_widget->unregister_local_shortcut_action(Badge(), *this); } void GAction::activate() diff --git a/LibGUI/GAction.h b/LibGUI/GAction.h index f691aeed50..1d8d65adce 100644 --- a/LibGUI/GAction.h +++ b/LibGUI/GAction.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,31 +13,40 @@ class GButton; class GMenuItem; +class GWidget; class GAction : public Retainable, public Weakable { public: - static Retained create(const String& text, Function callback) + enum class ShortcutScope { + None, + ApplicationGlobal, + WidgetLocal, + }; + static Retained create(const String& text, Function callback, GWidget* widget = nullptr) { - return adopt(*new GAction(text, move(callback))); + return adopt(*new GAction(text, move(callback), widget)); } - static Retained create(const String& text, const String& custom_data, Function callback) + static Retained create(const String& text, const String& custom_data, Function callback, GWidget* widget = nullptr) { - return adopt(*new GAction(text, custom_data, move(callback))); + return adopt(*new GAction(text, custom_data, move(callback), widget)); } - static Retained create(const String& text, RetainPtr&& icon, Function callback) + static Retained create(const String& text, RetainPtr&& icon, Function callback, GWidget* widget = nullptr) { - return adopt(*new GAction(text, move(icon), move(callback))); + return adopt(*new GAction(text, move(icon), move(callback), widget)); } - static Retained create(const String& text, const GShortcut& shortcut, Function callback) + static Retained create(const String& text, const GShortcut& shortcut, Function callback, GWidget* widget = nullptr) { - return adopt(*new GAction(text, shortcut, move(callback))); + return adopt(*new GAction(text, shortcut, move(callback), widget)); } - static Retained create(const String& text, const GShortcut& shortcut, RetainPtr&& icon, Function callback) + static Retained create(const String& text, const GShortcut& shortcut, RetainPtr&& icon, Function callback, GWidget* widget = nullptr) { - return adopt(*new GAction(text, shortcut, move(icon), move(callback))); + return adopt(*new GAction(text, shortcut, move(icon), move(callback), widget)); } ~GAction(); + GWidget* widget() { return m_widget.ptr(); } + const GWidget* widget() const { return m_widget.ptr(); } + String text() const { return m_text; } GShortcut shortcut() const { return m_shortcut; } String custom_data() const { return m_custom_data; } @@ -55,11 +65,11 @@ public: void unregister_menu_item(Badge, GMenuItem&); private: - GAction(const String& text, Function = nullptr); - GAction(const String& text, const GShortcut&, Function = nullptr); - GAction(const String& text, const GShortcut&, RetainPtr&& icon, Function = nullptr); - GAction(const String& text, RetainPtr&& icon, Function = nullptr); - GAction(const String& text, const String& custom_data = String(), Function = nullptr); + GAction(const String& text, Function = nullptr, GWidget* = nullptr); + GAction(const String& text, const GShortcut&, Function = nullptr, GWidget* = nullptr); + GAction(const String& text, const GShortcut&, RetainPtr&& icon, Function = nullptr, GWidget* = nullptr); + GAction(const String& text, RetainPtr&& icon, Function = nullptr, GWidget* = nullptr); + GAction(const String& text, const String& custom_data = String(), Function = nullptr, GWidget* = nullptr); template void for_each_toolbar_button(Callback); template void for_each_menu_item(Callback); @@ -69,8 +79,9 @@ private: RetainPtr m_icon; GShortcut m_shortcut; bool m_enabled { true }; + ShortcutScope m_scope { ShortcutScope::None }; HashTable m_buttons; HashTable m_menu_items; + WeakPtr m_widget; }; - diff --git a/LibGUI/GApplication.cpp b/LibGUI/GApplication.cpp index bdf68cf692..1a15476fd7 100644 --- a/LibGUI/GApplication.cpp +++ b/LibGUI/GApplication.cpp @@ -52,20 +52,20 @@ void GApplication::set_menubar(OwnPtr&& menubar) m_menubar->notify_added_to_application(Badge()); } -void GApplication::register_shortcut_action(Badge, GAction& action) +void GApplication::register_global_shortcut_action(Badge, GAction& action) { - m_shortcut_actions.set(action.shortcut(), &action); + m_global_shortcut_actions.set(action.shortcut(), &action); } -void GApplication::unregister_shortcut_action(Badge, GAction& action) +void GApplication::unregister_global_shortcut_action(Badge, GAction& action) { - m_shortcut_actions.remove(action.shortcut()); + m_global_shortcut_actions.remove(action.shortcut()); } GAction* GApplication::action_for_key_event(const GKeyEvent& event) { - auto it = m_shortcut_actions.find(GShortcut(event.modifiers(), (KeyCode)event.key())); - if (it == m_shortcut_actions.end()) + auto it = m_global_shortcut_actions.find(GShortcut(event.modifiers(), (KeyCode)event.key())); + if (it == m_global_shortcut_actions.end()) return nullptr; return (*it).value; } diff --git a/LibGUI/GApplication.h b/LibGUI/GApplication.h index baa31ba40d..db8d3764cc 100644 --- a/LibGUI/GApplication.h +++ b/LibGUI/GApplication.h @@ -23,8 +23,8 @@ public: void set_menubar(OwnPtr&&); GAction* action_for_key_event(const GKeyEvent&); - void register_shortcut_action(Badge, GAction&); - void unregister_shortcut_action(Badge, GAction&); + void register_global_shortcut_action(Badge, GAction&); + void unregister_global_shortcut_action(Badge, GAction&); void show_tooltip(const String&, const Point& screen_location); void hide_tooltip(); @@ -32,7 +32,7 @@ public: private: OwnPtr m_event_loop; OwnPtr m_menubar; - HashMap m_shortcut_actions; + HashMap m_global_shortcut_actions; class TooltipWindow; TooltipWindow* m_tooltip_window { nullptr }; }; diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index c03eff530c..17909a6494 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,15 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win key_event->m_text = String(&event.key.character, 1); if (event.type == WSAPI_ServerMessage::Type::KeyDown) { + if (auto* focused_widget = window.focused_widget()) { + if (auto* action = focused_widget->action_for_key_event(*key_event)) { + if (action->is_enabled()) { + action->activate(); + return; + } + } + } + if (auto* action = GApplication::the().action_for_key_event(*key_event)) { if (action->is_enabled()) { action->activate(); diff --git a/LibGUI/GTextEditor.cpp b/LibGUI/GTextEditor.cpp index 8fb1c047e9..70310de235 100644 --- a/LibGUI/GTextEditor.cpp +++ b/LibGUI/GTextEditor.cpp @@ -35,27 +35,27 @@ void GTextEditor::create_actions() { m_undo_action = GAction::create("Undo", { Mod_Ctrl, Key_Z }, GraphicsBitmap::load_from_file("/res/icons/16x16/undo.png"), [&] (const GAction&) { // FIXME: Undo - }); + }, this); m_redo_action = GAction::create("Redo", { Mod_Ctrl, Key_Y }, GraphicsBitmap::load_from_file("/res/icons/16x16/redo.png"), [&] (const GAction&) { // FIXME: Redo - }); + }, this); m_cut_action = GAction::create("Cut", { Mod_Ctrl, Key_X }, GraphicsBitmap::load_from_file("/res/icons/cut16.png"), [&] (const GAction&) { cut(); - }); + }, this); m_copy_action = GAction::create("Copy", { Mod_Ctrl, Key_C }, GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [&] (const GAction&) { copy(); - }); + }, this); m_paste_action = GAction::create("Paste", { Mod_Ctrl, Key_V }, GraphicsBitmap::load_from_file("/res/icons/paste16.png"), [&] (const GAction&) { paste(); - }); + }, this); m_delete_action = GAction::create("Delete", { 0, Key_Delete }, GraphicsBitmap::load_from_file("/res/icons/16x16/delete.png"), [&] (const GAction&) { do_delete(); - }); + }, this); } void GTextEditor::set_text(const String& text) diff --git a/LibGUI/GWidget.cpp b/LibGUI/GWidget.cpp index 901b2f5d6a..fe95a9de1a 100644 --- a/LibGUI/GWidget.cpp +++ b/LibGUI/GWidget.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -487,3 +488,21 @@ bool GWidget::is_backmost() const return true; return parent->children().first() == this; } + +GAction* GWidget::action_for_key_event(const GKeyEvent& event) +{ + auto it = m_local_shortcut_actions.find(GShortcut(event.modifiers(), (KeyCode)event.key())); + if (it == m_local_shortcut_actions.end()) + return nullptr; + return (*it).value; +} + +void GWidget::register_local_shortcut_action(Badge, GAction& action) +{ + m_local_shortcut_actions.set(action.shortcut(), &action); +} + +void GWidget::unregister_local_shortcut_action(Badge, GAction& action) +{ + m_local_shortcut_actions.remove(action.shortcut()); +} diff --git a/LibGUI/GWidget.h b/LibGUI/GWidget.h index 6198365dfe..eebf6e0a17 100644 --- a/LibGUI/GWidget.h +++ b/LibGUI/GWidget.h @@ -2,14 +2,17 @@ #include #include +#include #include #include #include #include #include #include +#include class GraphicsBitmap; +class GAction; class GLayout; class GMenu; class GWindow; @@ -172,6 +175,11 @@ public: bool is_frontmost() const; bool is_backmost() const; + GAction* action_for_key_event(const GKeyEvent&); + + void register_local_shortcut_action(Badge, GAction&); + void unregister_local_shortcut_action(Badge, GAction&); + private: virtual bool is_widget() const final { return true; } @@ -203,4 +211,6 @@ private: bool m_layout_dirty { false }; CElapsedTimer m_click_clock; + + HashMap m_local_shortcut_actions; };