diff --git a/Libraries/LibCore/CObject.h b/Libraries/LibCore/CObject.h index a98bfc9edc..df4ad4b815 100644 --- a/Libraries/LibCore/CObject.h +++ b/Libraries/LibCore/CObject.h @@ -115,6 +115,7 @@ public: void deferred_invoke(Function); bool is_widget() const { return m_widget; } + virtual bool is_action() const { return false; } virtual bool is_window() const { return false; } virtual void save_to(AK::JsonObject&); diff --git a/Libraries/LibGUI/GAction.cpp b/Libraries/LibGUI/GAction.cpp index 452f20eaeb..86468761a6 100644 --- a/Libraries/LibGUI/GAction.cpp +++ b/Libraries/LibGUI/GAction.cpp @@ -32,54 +32,54 @@ namespace GCommonActions { -NonnullRefPtr make_open_action(Function callback, GWidget* widget) +NonnullRefPtr make_open_action(Function callback, CObject* parent) { - return GAction::create("Open...", { Mod_Ctrl, Key_O }, GraphicsBitmap::load_from_file("/res/icons/16x16/open.png"), move(callback), widget); + return GAction::create("Open...", { Mod_Ctrl, Key_O }, GraphicsBitmap::load_from_file("/res/icons/16x16/open.png"), move(callback), parent); } -NonnullRefPtr make_move_to_front_action(Function callback, GWidget* widget) +NonnullRefPtr make_move_to_front_action(Function callback, CObject* parent) { - return GAction::create("Move to front", { Mod_Ctrl | Mod_Shift, Key_Up }, GraphicsBitmap::load_from_file("/res/icons/16x16/move-to-front.png"), move(callback), widget); + return GAction::create("Move to front", { Mod_Ctrl | Mod_Shift, Key_Up }, GraphicsBitmap::load_from_file("/res/icons/16x16/move-to-front.png"), move(callback), parent); } -NonnullRefPtr make_move_to_back_action(Function callback, GWidget* widget) +NonnullRefPtr make_move_to_back_action(Function callback, CObject* parent) { - return GAction::create("Move to back", { Mod_Ctrl | Mod_Shift, Key_Down }, GraphicsBitmap::load_from_file("/res/icons/16x16/move-to-back.png"), move(callback), widget); + return GAction::create("Move to back", { Mod_Ctrl | Mod_Shift, Key_Down }, GraphicsBitmap::load_from_file("/res/icons/16x16/move-to-back.png"), move(callback), parent); } -NonnullRefPtr make_undo_action(Function callback, GWidget* widget) +NonnullRefPtr make_undo_action(Function callback, CObject* parent) { - return GAction::create("Undo", { Mod_Ctrl, Key_Z }, GraphicsBitmap::load_from_file("/res/icons/16x16/undo.png"), move(callback), widget); + return GAction::create("Undo", { Mod_Ctrl, Key_Z }, GraphicsBitmap::load_from_file("/res/icons/16x16/undo.png"), move(callback), parent); } -NonnullRefPtr make_redo_action(Function callback, GWidget* widget) +NonnullRefPtr make_redo_action(Function callback, CObject* parent) { - return GAction::create("Redo", { Mod_Ctrl, Key_Y }, GraphicsBitmap::load_from_file("/res/icons/16x16/redo.png"), move(callback), widget); + return GAction::create("Redo", { Mod_Ctrl, Key_Y }, GraphicsBitmap::load_from_file("/res/icons/16x16/redo.png"), move(callback), parent); } -NonnullRefPtr make_delete_action(Function callback, GWidget* widget) +NonnullRefPtr make_delete_action(Function callback, CObject* parent) { - return GAction::create("Delete", { Mod_None, Key_Delete }, GraphicsBitmap::load_from_file("/res/icons/16x16/delete.png"), move(callback), widget); + return GAction::create("Delete", { Mod_None, Key_Delete }, GraphicsBitmap::load_from_file("/res/icons/16x16/delete.png"), move(callback), parent); } -NonnullRefPtr make_cut_action(Function callback, GWidget* widget) +NonnullRefPtr make_cut_action(Function callback, CObject* parent) { - return GAction::create("Cut", { Mod_Ctrl, Key_X }, GraphicsBitmap::load_from_file("/res/icons/cut16.png"), move(callback), widget); + return GAction::create("Cut", { Mod_Ctrl, Key_X }, GraphicsBitmap::load_from_file("/res/icons/cut16.png"), move(callback), parent); } -NonnullRefPtr make_copy_action(Function callback, GWidget* widget) +NonnullRefPtr make_copy_action(Function callback, CObject* parent) { - return GAction::create("Copy", { Mod_Ctrl, Key_C }, GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), move(callback), widget); + return GAction::create("Copy", { Mod_Ctrl, Key_C }, GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), move(callback), parent); } -NonnullRefPtr make_paste_action(Function callback, GWidget* widget) +NonnullRefPtr make_paste_action(Function callback, CObject* parent) { - return GAction::create("Paste", { Mod_Ctrl, Key_V }, GraphicsBitmap::load_from_file("/res/icons/paste16.png"), move(callback), widget); + return GAction::create("Paste", { Mod_Ctrl, Key_V }, GraphicsBitmap::load_from_file("/res/icons/paste16.png"), move(callback), parent); } -NonnullRefPtr make_fullscreen_action(Function callback, GWidget* widget) +NonnullRefPtr make_fullscreen_action(Function callback, CObject* parent) { - return GAction::create("Fullscreen", { Mod_None, Key_F11 }, move(callback), widget); + return GAction::create("Fullscreen", { Mod_None, Key_F11 }, move(callback), parent); } NonnullRefPtr make_quit_action(Function callback) @@ -87,58 +87,59 @@ NonnullRefPtr make_quit_action(Function callback) return GAction::create("Quit", { Mod_Alt, Key_F4 }, move(callback)); } -NonnullRefPtr make_go_back_action(Function callback, GWidget* widget) +NonnullRefPtr make_go_back_action(Function callback, CObject* parent) { - return GAction::create("Go back", { Mod_Alt, Key_Left }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-back.png"), move(callback), widget); + return GAction::create("Go back", { Mod_Alt, Key_Left }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-back.png"), move(callback), parent); } -NonnullRefPtr make_go_forward_action(Function callback, GWidget* widget) +NonnullRefPtr make_go_forward_action(Function callback, CObject* parent) { - return GAction::create("Go forward", { Mod_Alt, Key_Right }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), move(callback), widget); + return GAction::create("Go forward", { Mod_Alt, Key_Right }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-forward.png"), move(callback), parent); } -NonnullRefPtr make_go_home_action(Function callback, GWidget* widget) +NonnullRefPtr make_go_home_action(Function callback, CObject* parent) { - return GAction::create("Go home", { Mod_Alt, Key_Home }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-home.png"), move(callback), widget); + return GAction::create("Go home", { Mod_Alt, Key_Home }, GraphicsBitmap::load_from_file("/res/icons/16x16/go-home.png"), move(callback), parent); } -NonnullRefPtr make_reload_action(Function callback, GWidget* widget) +NonnullRefPtr make_reload_action(Function callback, CObject* parent) { - return GAction::create("Reload", { Mod_Ctrl, Key_R }, GraphicsBitmap::load_from_file("/res/icons/16x16/reload.png"), move(callback), widget); + return GAction::create("Reload", { Mod_Ctrl, Key_R }, GraphicsBitmap::load_from_file("/res/icons/16x16/reload.png"), move(callback), parent); } } -GAction::GAction(const StringView& text, Function on_activation_callback, GWidget* widget) - : on_activation(move(on_activation_callback)) +GAction::GAction(const StringView& text, Function on_activation_callback, CObject* parent) + : CObject(parent) + , on_activation(move(on_activation_callback)) , m_text(text) - , m_widget(widget ? widget->make_weak_ptr() : nullptr) { } -GAction::GAction(const StringView& text, RefPtr&& icon, Function on_activation_callback, GWidget* widget) - : on_activation(move(on_activation_callback)) +GAction::GAction(const StringView& text, RefPtr&& icon, Function on_activation_callback, CObject* parent) + : CObject(parent) + , on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) - , m_widget(widget ? widget->make_weak_ptr() : nullptr) { } -GAction::GAction(const StringView& text, const GShortcut& shortcut, Function on_activation_callback, GWidget* widget) - : GAction(text, shortcut, nullptr, move(on_activation_callback), widget) +GAction::GAction(const StringView& text, const GShortcut& shortcut, Function on_activation_callback, CObject* parent) + : GAction(text, shortcut, nullptr, move(on_activation_callback), parent) { } -GAction::GAction(const StringView& text, const GShortcut& shortcut, RefPtr&& icon, Function on_activation_callback, GWidget* widget) - : on_activation(move(on_activation_callback)) +GAction::GAction(const StringView& text, const GShortcut& shortcut, RefPtr&& icon, Function on_activation_callback, CObject* parent) + : CObject(parent) + , on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) , m_shortcut(shortcut) - , m_widget(widget ? widget->make_weak_ptr() : nullptr) { - if (m_widget) { + if (parent && is(*parent)) { m_scope = ShortcutScope::WidgetLocal; - m_widget->register_local_shortcut_action({}, *this); + } else if (parent && is(*parent)) { + m_scope = ShortcutScope::WindowLocal; } else { m_scope = ShortcutScope::ApplicationGlobal; GApplication::the().register_global_shortcut_action({}, *this); @@ -149,8 +150,6 @@ GAction::~GAction() { if (m_shortcut.is_valid() && m_scope == ShortcutScope::ApplicationGlobal) GApplication::the().unregister_global_shortcut_action({}, *this); - if (m_widget && m_scope == ShortcutScope::WidgetLocal) - m_widget->unregister_local_shortcut_action({}, *this); } void GAction::activate(CObject* activator) diff --git a/Libraries/LibGUI/GAction.h b/Libraries/LibGUI/GAction.h index c8190d0a40..31827f66d0 100644 --- a/Libraries/LibGUI/GAction.h +++ b/Libraries/LibGUI/GAction.h @@ -42,53 +42,51 @@ class GAction; class GActionGroup; class GButton; class GMenuItem; -class GWidget; namespace GCommonActions { -NonnullRefPtr make_open_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_undo_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_redo_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_cut_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_copy_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_paste_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_delete_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_move_to_front_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_move_to_back_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_fullscreen_action(Function, GWidget* widget = nullptr); +NonnullRefPtr make_open_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_undo_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_redo_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_cut_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_copy_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_paste_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_delete_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_move_to_front_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_move_to_back_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_fullscreen_action(Function, CObject* parent = nullptr); NonnullRefPtr make_quit_action(Function); -NonnullRefPtr make_go_back_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_go_forward_action(Function, GWidget* widget = nullptr); -NonnullRefPtr make_go_home_action(Function callback, GWidget* widget = nullptr); -NonnullRefPtr make_reload_action(Function, GWidget* widget = nullptr); +NonnullRefPtr make_go_back_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_go_forward_action(Function, CObject* parent = nullptr); +NonnullRefPtr make_go_home_action(Function callback, CObject* parent = nullptr); +NonnullRefPtr make_reload_action(Function, CObject* parent = nullptr); }; -class GAction : public RefCounted - , public Weakable { +class GAction final : public CObject { + C_OBJECT(GAction) public: enum class ShortcutScope { None, - ApplicationGlobal, WidgetLocal, + WindowLocal, + ApplicationGlobal, }; - static NonnullRefPtr create(const StringView& text, Function callback, GWidget* widget = nullptr) + static NonnullRefPtr create(const StringView& text, Function callback, CObject* parent = nullptr) { - return adopt(*new GAction(text, move(callback), widget)); + return adopt(*new GAction(text, move(callback), parent)); } - static NonnullRefPtr create(const StringView& text, RefPtr&& icon, Function callback, GWidget* widget = nullptr) + static NonnullRefPtr create(const StringView& text, RefPtr&& icon, Function callback, CObject* parent = nullptr) { - return adopt(*new GAction(text, move(icon), move(callback), widget)); + return adopt(*new GAction(text, move(icon), move(callback), parent)); } - static NonnullRefPtr create(const StringView& text, const GShortcut& shortcut, Function callback, GWidget* widget = nullptr) + static NonnullRefPtr create(const StringView& text, const GShortcut& shortcut, Function callback, CObject* parent = nullptr) { - return adopt(*new GAction(text, shortcut, move(callback), widget)); + return adopt(*new GAction(text, shortcut, move(callback), parent)); } - static NonnullRefPtr create(const StringView& text, const GShortcut& shortcut, RefPtr&& icon, Function callback, GWidget* widget = nullptr) + static NonnullRefPtr create(const StringView& text, const GShortcut& shortcut, RefPtr&& icon, Function callback, CObject* parent = nullptr) { - return adopt(*new GAction(text, shortcut, move(icon), move(callback), widget)); + return adopt(*new GAction(text, shortcut, move(icon), move(callback), parent)); } - ~GAction(); - GWidget* widget() { return m_widget.ptr(); } - const GWidget* widget() const { return m_widget.ptr(); } + virtual ~GAction() override; String text() const { return m_text; } GShortcut shortcut() const { return m_shortcut; } @@ -124,10 +122,12 @@ public: void set_group(Badge, GActionGroup*); private: - GAction(const StringView& text, Function = nullptr, GWidget* = nullptr); - GAction(const StringView& text, const GShortcut&, Function = nullptr, GWidget* = nullptr); - GAction(const StringView& text, const GShortcut&, RefPtr&& icon, Function = nullptr, GWidget* = nullptr); - GAction(const StringView& text, RefPtr&& icon, Function = nullptr, GWidget* = nullptr); + virtual bool is_action() const override { return true; } + + GAction(const StringView& text, Function = nullptr, CObject* = nullptr); + GAction(const StringView& text, const GShortcut&, Function = nullptr, CObject* = nullptr); + GAction(const StringView& text, const GShortcut&, RefPtr&& icon, Function = nullptr, CObject* = nullptr); + GAction(const StringView& text, RefPtr&& icon, Function = nullptr, CObject* = nullptr); template void for_each_toolbar_button(Callback); @@ -144,7 +144,12 @@ private: HashTable m_buttons; HashTable m_menu_items; - WeakPtr m_widget; WeakPtr m_action_group; WeakPtr m_activator; }; + +template<> +inline bool is(const CObject& object) +{ + return object.is_action(); +} diff --git a/Libraries/LibGUI/GWidget.cpp b/Libraries/LibGUI/GWidget.cpp index a16ea0ce82..0ee5a83356 100644 --- a/Libraries/LibGUI/GWidget.cpp +++ b/Libraries/LibGUI/GWidget.cpp @@ -626,20 +626,16 @@ bool GWidget::is_backmost() const 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()); + GShortcut shortcut(event.modifiers(), (KeyCode)event.key()); + GAction* found_action = nullptr; + for_each_child_of_type([&] (auto& action) { + if (action.shortcut() == shortcut) { + found_action = &action; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return found_action; } void GWidget::set_updates_enabled(bool enabled) diff --git a/Libraries/LibGUI/GWidget.h b/Libraries/LibGUI/GWidget.h index 7e0488a57b..f9a3921564 100644 --- a/Libraries/LibGUI/GWidget.h +++ b/Libraries/LibGUI/GWidget.h @@ -241,9 +241,6 @@ public: GAction* action_for_key_event(const GKeyEvent&); - void register_local_shortcut_action(Badge, GAction&); - void unregister_local_shortcut_action(Badge, GAction&); - template void for_each_child_widget(Callback callback) { @@ -325,8 +322,6 @@ private: bool m_layout_dirty { false }; bool m_updates_enabled { true }; - HashMap m_local_shortcut_actions; - NonnullRefPtr m_palette; }; diff --git a/Libraries/LibGUI/GWindow.cpp b/Libraries/LibGUI/GWindow.cpp index f91a790c62..4bab839163 100644 --- a/Libraries/LibGUI/GWindow.cpp +++ b/Libraries/LibGUI/GWindow.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -633,3 +634,17 @@ void GWindow::notify_state_changed(Badge, bool minimize } } } + +GAction* GWindow::action_for_key_event(const GKeyEvent& event) +{ + GShortcut shortcut(event.modifiers(), (KeyCode)event.key()); + GAction* found_action = nullptr; + for_each_child_of_type([&](auto& action) { + if (action.shortcut() == shortcut) { + found_action = &action; + return IterationDecision::Break; + } + return IterationDecision::Continue; + }); + return found_action; +} diff --git a/Libraries/LibGUI/GWindow.h b/Libraries/LibGUI/GWindow.h index 92f5de9c5d..ae47f1efe8 100644 --- a/Libraries/LibGUI/GWindow.h +++ b/Libraries/LibGUI/GWindow.h @@ -35,6 +35,8 @@ #include #include +class GAction; +class GKeyEvent; class GWMEvent; class GWidget; class GWindowServerConnection; @@ -170,6 +172,8 @@ public: virtual bool is_visible_for_timer_purposes() const override { return m_visible_for_timer_purposes; } + GAction* action_for_key_event(const GKeyEvent&); + protected: GWindow(CObject* parent = nullptr); virtual void wm_event(GWMEvent&); @@ -210,3 +214,9 @@ private: bool m_layout_pending { false }; bool m_visible_for_timer_purposes { true }; }; + +template<> +inline bool is(const CObject& object) +{ + return object.is_window(); +} diff --git a/Libraries/LibGUI/GWindowServerConnection.cpp b/Libraries/LibGUI/GWindowServerConnection.cpp index 808b4be0a9..c8ac8c775f 100644 --- a/Libraries/LibGUI/GWindowServerConnection.cpp +++ b/Libraries/LibGUI/GWindowServerConnection.cpp @@ -136,20 +136,20 @@ void GWindowServerConnection::handle(const WindowClient::KeyDown& message) key_event->m_text = String(&ch, 1); } - 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; - } - } - } + GAction* action = nullptr; - if (auto* action = GApplication::the().action_for_key_event(*key_event)) { - if (action->is_enabled()) { - action->activate(); - return; - } + if (auto* focused_widget = window->focused_widget()) + action = focused_widget->action_for_key_event(*key_event); + + if (!action) + action = window->action_for_key_event(*key_event); + + if (!action) + action = GApplication::the().action_for_key_event(*key_event); + + if (action && action->is_enabled()) { + action->activate(); + return; } CEventLoop::current().post_event(*window, move(key_event)); }