Serenity Operating System
at master 312 lines 9.9 kB view raw
1/* 2 * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <LibGUI/Action.h> 8#include <LibGUI/ActionGroup.h> 9#include <LibGUI/Application.h> 10#include <LibGUI/Button.h> 11#include <LibGUI/MenuItem.h> 12#include <LibGUI/Window.h> 13 14namespace GUI { 15 16NonnullRefPtr<Action> Action::create(DeprecatedString text, Function<void(Action&)> callback, Core::Object* parent) 17{ 18 return adopt_ref(*new Action(move(text), move(callback), parent)); 19} 20 21NonnullRefPtr<Action> Action::create(DeprecatedString text, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> callback, Core::Object* parent) 22{ 23 return adopt_ref(*new Action(move(text), move(icon), move(callback), parent)); 24} 25 26NonnullRefPtr<Action> Action::create(DeprecatedString text, Shortcut const& shortcut, Function<void(Action&)> callback, Core::Object* parent) 27{ 28 return adopt_ref(*new Action(move(text), shortcut, move(callback), parent)); 29} 30 31NonnullRefPtr<Action> Action::create(DeprecatedString text, Shortcut const& shortcut, Shortcut const& alternate_shortcut, Function<void(Action&)> callback, Core::Object* parent) 32{ 33 return adopt_ref(*new Action(move(text), shortcut, alternate_shortcut, move(callback), parent)); 34} 35 36NonnullRefPtr<Action> Action::create(DeprecatedString text, Shortcut const& shortcut, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> callback, Core::Object* parent) 37{ 38 return adopt_ref(*new Action(move(text), shortcut, Shortcut {}, move(icon), move(callback), parent)); 39} 40 41NonnullRefPtr<Action> Action::create(DeprecatedString text, Shortcut const& shortcut, Shortcut const& alternate_shortcut, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> callback, Core::Object* parent) 42{ 43 return adopt_ref(*new Action(move(text), shortcut, alternate_shortcut, move(icon), move(callback), parent)); 44} 45 46NonnullRefPtr<Action> Action::create_checkable(DeprecatedString text, Function<void(Action&)> callback, Core::Object* parent) 47{ 48 return adopt_ref(*new Action(move(text), move(callback), parent, true)); 49} 50 51NonnullRefPtr<Action> Action::create_checkable(DeprecatedString text, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> callback, Core::Object* parent) 52{ 53 return adopt_ref(*new Action(move(text), move(icon), move(callback), parent, true)); 54} 55 56NonnullRefPtr<Action> Action::create_checkable(DeprecatedString text, Shortcut const& shortcut, Function<void(Action&)> callback, Core::Object* parent) 57{ 58 return adopt_ref(*new Action(move(text), shortcut, move(callback), parent, true)); 59} 60 61NonnullRefPtr<Action> Action::create_checkable(DeprecatedString text, Shortcut const& shortcut, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> callback, Core::Object* parent) 62{ 63 return adopt_ref(*new Action(move(text), shortcut, Shortcut {}, move(icon), move(callback), parent, true)); 64} 65 66ErrorOr<NonnullRefPtr<Action>> Action::try_create_checkable(DeprecatedString text, Shortcut const& shortcut, Function<void(Action&)> callback, Core::Object* parent) 67{ 68 return adopt_nonnull_ref_or_enomem(new (nothrow) Action(move(text), shortcut, Shortcut {}, move(callback), parent, true)); 69} 70 71RefPtr<Action> Action::find_action_for_shortcut(Core::Object& object, Shortcut const& shortcut) 72{ 73 RefPtr<Action> found_action = nullptr; 74 object.for_each_child_of_type<Action>([&](auto& action) { 75 if (action.shortcut() == shortcut || action.alternate_shortcut() == shortcut) { 76 found_action = &action; 77 return IterationDecision::Break; 78 } 79 return IterationDecision::Continue; 80 }); 81 return found_action; 82} 83 84Action::Action(DeprecatedString text, Function<void(Action&)> on_activation_callback, Core::Object* parent, bool checkable) 85 : Action(move(text), Shortcut {}, Shortcut {}, nullptr, move(on_activation_callback), parent, checkable) 86{ 87} 88 89Action::Action(DeprecatedString text, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> on_activation_callback, Core::Object* parent, bool checkable) 90 : Action(move(text), Shortcut {}, Shortcut {}, move(icon), move(on_activation_callback), parent, checkable) 91{ 92} 93 94Action::Action(DeprecatedString text, Shortcut const& shortcut, Function<void(Action&)> on_activation_callback, Core::Object* parent, bool checkable) 95 : Action(move(text), shortcut, Shortcut {}, nullptr, move(on_activation_callback), parent, checkable) 96{ 97} 98 99Action::Action(DeprecatedString text, Shortcut const& shortcut, Shortcut const& alternate_shortcut, Function<void(Action&)> on_activation_callback, Core::Object* parent, bool checkable) 100 : Action(move(text), shortcut, alternate_shortcut, nullptr, move(on_activation_callback), parent, checkable) 101{ 102} 103 104Action::Action(DeprecatedString text, Shortcut const& shortcut, Shortcut const& alternate_shortcut, RefPtr<Gfx::Bitmap const> icon, Function<void(Action&)> on_activation_callback, Core::Object* parent, bool checkable) 105 : Core::Object(parent) 106 , on_activation(move(on_activation_callback)) 107 , m_text(move(text)) 108 , m_icon(move(icon)) 109 , m_shortcut(shortcut) 110 , m_alternate_shortcut(alternate_shortcut) 111 , m_checkable(checkable) 112{ 113 if (parent && is<Widget>(*parent)) { 114 m_scope = ShortcutScope::WidgetLocal; 115 } else if (parent && is<Window>(*parent)) { 116 m_scope = ShortcutScope::WindowLocal; 117 } else { 118 m_scope = ShortcutScope::ApplicationGlobal; 119 if (auto* app = Application::the()) { 120 app->register_global_shortcut_action({}, *this); 121 } 122 } 123} 124 125Action::~Action() 126{ 127 if (m_scope == ShortcutScope::ApplicationGlobal) { 128 if (auto* app = Application::the()) 129 app->unregister_global_shortcut_action({}, *this); 130 } 131} 132 133void Action::process_event(Window& window, Event& event) 134{ 135 if (is_enabled()) { 136 flash_menubar_menu(window); 137 activate(); 138 event.accept(); 139 return; 140 } 141 if (swallow_key_event_when_disabled()) { 142 event.accept(); 143 return; 144 } 145 146 event.ignore(); 147} 148 149void Action::activate(Core::Object* activator) 150{ 151 if (!on_activation) 152 return; 153 154 if (activator) 155 m_activator = activator->make_weak_ptr(); 156 157 if (is_checkable()) { 158 if (m_action_group) { 159 if (m_action_group->is_unchecking_allowed()) 160 set_checked(!is_checked()); 161 else 162 set_checked(true); 163 } else { 164 set_checked(!is_checked()); 165 } 166 } 167 168 if (activator == nullptr) { 169 for_each_toolbar_button([](auto& button) { 170 button.mimic_pressed(); 171 }); 172 } 173 174 on_activation(*this); 175 m_activator = nullptr; 176} 177 178void Action::flash_menubar_menu(GUI::Window& window) 179{ 180 for (auto& menu_item : m_menu_items) 181 window.flash_menubar_menu_for(*menu_item); 182} 183 184void Action::register_button(Badge<Button>, Button& button) 185{ 186 m_buttons.set(&button); 187} 188 189void Action::unregister_button(Badge<Button>, Button& button) 190{ 191 m_buttons.remove(&button); 192} 193 194void Action::register_menu_item(Badge<MenuItem>, MenuItem& menu_item) 195{ 196 m_menu_items.set(&menu_item); 197} 198 199void Action::unregister_menu_item(Badge<MenuItem>, MenuItem& menu_item) 200{ 201 m_menu_items.remove(&menu_item); 202} 203 204template<typename Callback> 205void Action::for_each_toolbar_button(Callback callback) 206{ 207 for (auto& it : m_buttons) 208 callback(*it); 209} 210 211template<typename Callback> 212void Action::for_each_menu_item(Callback callback) 213{ 214 for (auto& it : m_menu_items) 215 callback(*it); 216} 217 218void Action::set_enabled(bool enabled) 219{ 220 if (m_enabled == enabled) 221 return; 222 m_enabled = enabled; 223 for_each_toolbar_button([enabled](auto& button) { 224 button.set_enabled(enabled); 225 }); 226 for_each_menu_item([enabled](auto& item) { 227 item.set_enabled(enabled); 228 }); 229} 230 231void Action::set_visible(bool visible) 232{ 233 if (m_visible == visible) 234 return; 235 m_visible = visible; 236 for_each_toolbar_button([visible](auto& button) { 237 button.set_visible(visible); 238 }); 239 for_each_menu_item([visible](auto& item) { 240 item.set_visible(visible); 241 }); 242} 243 244void Action::set_checked(bool checked) 245{ 246 if (m_checked == checked) 247 return; 248 m_checked = checked; 249 250 if (m_checked && m_action_group) { 251 m_action_group->for_each_action([this](auto& other_action) { 252 if (this == &other_action) 253 return IterationDecision::Continue; 254 if (other_action.is_checkable()) 255 other_action.set_checked(false); 256 return IterationDecision::Continue; 257 }); 258 } 259 260 for_each_toolbar_button([checked](auto& button) { 261 button.set_checked(checked); 262 }); 263 for_each_menu_item([checked](MenuItem& item) { 264 item.set_checked(checked); 265 }); 266} 267 268void Action::set_group(Badge<ActionGroup>, ActionGroup* group) 269{ 270 m_action_group = AK::make_weak_ptr_if_nonnull(group); 271} 272 273void Action::set_icon(Gfx::Bitmap const* icon) 274{ 275 if (m_icon == icon) 276 return; 277 m_icon = icon; 278 for_each_toolbar_button([icon](auto& button) { 279 button.set_icon(icon); 280 }); 281 for_each_menu_item([](auto& menu_item) { 282 menu_item.update_from_action({}); 283 }); 284} 285 286void Action::set_text(DeprecatedString text) 287{ 288 if (m_text == text) 289 return; 290 m_text = move(text); 291 for_each_toolbar_button([&](auto& button) { 292 button.set_text(String::from_deprecated_string(m_text).release_value_but_fixme_should_propagate_errors()); 293 }); 294 for_each_menu_item([&](auto& menu_item) { 295 menu_item.update_from_action({}); 296 }); 297} 298 299void Action::set_tooltip(DeprecatedString tooltip) 300{ 301 if (m_tooltip == tooltip) 302 return; 303 m_tooltip = move(tooltip); 304 for_each_toolbar_button([&](auto& button) { 305 button.set_tooltip(*m_tooltip); 306 }); 307 for_each_menu_item([&](auto& menu_item) { 308 menu_item.update_from_action({}); 309 }); 310} 311 312}