From 705cee528a803b1671d16eeaf222d3318708500b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 21 Apr 2020 17:19:27 +0200 Subject: [PATCH] LibGUI: Make it easier to create checkable GUI::Actions This patch adds GUI::Action::create_checkable() helpers that work just like the existing create() helpers, but the actions become checkable(!) Clients are no longer required to manage the checked state of their actions manually, but instead they will be checked/unchecked as needed by GUI::Action itself before the activation hook is fired. --- Applications/Browser/main.cpp | 8 +- Applications/FileManager/main.cpp | 15 +- Applications/PaintBrush/EllipseTool.cpp | 6 +- Applications/PaintBrush/EraseTool.cpp | 11 +- Applications/PaintBrush/LineTool.cpp | 4 +- Applications/PaintBrush/PenTool.cpp | 4 +- Applications/PaintBrush/SprayTool.cpp | 4 +- Applications/SoundPlayer/main.cpp | 4 +- Applications/SystemMonitor/main.cpp | 4 +- Applications/Terminal/main.cpp | 4 +- Applications/TextEditor/TextEditorWidget.cpp | 16 +- DevTools/HackStudio/main.cpp | 6 +- DevTools/ProfileViewer/main.cpp | 8 +- Games/Minesweeper/main.cpp | 7 +- Libraries/LibGUI/AbstractTableView.cpp | 4 +- Libraries/LibGUI/Action.cpp | 151 +++++++++++-------- Libraries/LibGUI/Action.h | 25 ++- Libraries/LibGUI/MultiView.cpp | 12 +- 18 files changed, 135 insertions(+), 158 deletions(-) diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index 06a88877eaf..1ed5d03d3bd 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -288,22 +288,18 @@ int main(int argc, char** argv) } })); debug_menu.add_separator(); - auto line_box_borders_action = GUI::Action::create("Line box borders", [&](auto& action) { - action.set_checked(!action.is_checked()); + auto line_box_borders_action = GUI::Action::create_checkable("Line box borders", [&](auto& action) { html_widget.set_should_show_line_box_borders(action.is_checked()); html_widget.update(); }); - line_box_borders_action->set_checkable(true); line_box_borders_action->set_checked(false); debug_menu.add_action(line_box_borders_action); auto& bookmarks_menu = menubar->add_menu("Bookmarks"); - auto show_bookmarksbar_action = GUI::Action::create("Show bookmarks bar", [&](auto& action) { - action.set_checked(!action.is_checked()); + auto show_bookmarksbar_action = GUI::Action::create_checkable("Show bookmarks bar", [&](auto& action) { bookmarksbar.set_visible(action.is_checked()); bookmarksbar.update(); }); - show_bookmarksbar_action->set_checkable(true); show_bookmarksbar_action->set_checked(bookmarksbar_enabled); bookmarks_menu.add_action(show_bookmarksbar_action); diff --git a/Applications/FileManager/main.cpp b/Applications/FileManager/main.cpp index c37b8d3deb4..d0c1044c147 100644 --- a/Applications/FileManager/main.cpp +++ b/Applications/FileManager/main.cpp @@ -283,38 +283,29 @@ int run_in_windowed_mode(RefPtr config, String initial_locatio RefPtr view_as_icons_action; RefPtr view_as_columns_action; - view_as_table_action = GUI::Action::create( + view_as_table_action = GUI::Action::create_checkable( "Table view", { Mod_Ctrl, KeyCode::Key_L }, Gfx::Bitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GUI::Action&) { directory_view.set_view_mode(DirectoryView::ViewMode::List); - view_as_table_action->set_checked(true); - config->write_entry("DirectoryView", "ViewMode", "List"); config->sync(); }, window); - view_as_table_action->set_checkable(true); - view_as_icons_action = GUI::Action::create( + view_as_icons_action = GUI::Action::create_checkable( "Icon view", { Mod_Ctrl, KeyCode::Key_I }, Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GUI::Action&) { directory_view.set_view_mode(DirectoryView::ViewMode::Icon); - view_as_icons_action->set_checked(true); - config->write_entry("DirectoryView", "ViewMode", "Icon"); config->sync(); }, window); - view_as_icons_action->set_checkable(true); - view_as_columns_action = GUI::Action::create( + view_as_columns_action = GUI::Action::create_checkable( "Columns view", Gfx::Bitmap::load_from_file("/res/icons/16x16/columns-view.png"), [&](const GUI::Action&) { directory_view.set_view_mode(DirectoryView::ViewMode::Columns); - view_as_columns_action->set_checked(true); - config->write_entry("DirectoryView", "ViewMode", "Columns"); config->sync(); }, window); - view_as_columns_action->set_checkable(true); auto view_type_action_group = make(); view_type_action_group->set_exclusive(true); diff --git a/Applications/PaintBrush/EllipseTool.cpp b/Applications/PaintBrush/EllipseTool.cpp index 0824d72b3d7..b0f5f80b5a9 100644 --- a/Applications/PaintBrush/EllipseTool.cpp +++ b/Applications/PaintBrush/EllipseTool.cpp @@ -26,10 +26,10 @@ #include "EllipseTool.h" #include "PaintableWidget.h" -#include #include #include #include +#include #include EllipseTool::EllipseTool() @@ -117,11 +117,9 @@ void EllipseTool::on_contextmenu(GUI::ContextMenuEvent& event) m_context_menu->add_separator(); m_thickness_actions.set_exclusive(true); auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create(String::number(size), [this, size](auto& action) { + auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { m_thickness = size; - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); m_thickness_actions.add_action(*action); m_context_menu->add_action(move(action)); diff --git a/Applications/PaintBrush/EraseTool.cpp b/Applications/PaintBrush/EraseTool.cpp index 4a6326d5253..6b21a361163 100644 --- a/Applications/PaintBrush/EraseTool.cpp +++ b/Applications/PaintBrush/EraseTool.cpp @@ -77,12 +77,9 @@ void EraseTool::on_contextmenu(GUI::ContextMenuEvent& event) if (!m_context_menu) { m_context_menu = GUI::Menu::construct(); - NonnullRefPtr eraser_color_toggler = GUI::Action::create("Use secondary color", [&](GUI::Action& action) { - bool toggled = !m_use_secondary_color; - m_use_secondary_color = toggled; - action.set_checked(toggled); + auto eraser_color_toggler = GUI::Action::create_checkable("Use secondary color", [&](auto& action) { + m_use_secondary_color = action.is_checked(); }); - eraser_color_toggler->set_checkable(true); eraser_color_toggler->set_checked(m_use_secondary_color); m_context_menu->add_action(eraser_color_toggler); @@ -90,11 +87,9 @@ void EraseTool::on_contextmenu(GUI::ContextMenuEvent& event) m_thickness_actions.set_exclusive(true); auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create(String::number(size), [this, size](auto& action) { + auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { m_thickness = size; - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); m_thickness_actions.add_action(*action); m_context_menu->add_action(move(action)); diff --git a/Applications/PaintBrush/LineTool.cpp b/Applications/PaintBrush/LineTool.cpp index 8984f52ccd6..f5efb126d0a 100644 --- a/Applications/PaintBrush/LineTool.cpp +++ b/Applications/PaintBrush/LineTool.cpp @@ -133,11 +133,9 @@ void LineTool::on_contextmenu(GUI::ContextMenuEvent& event) m_context_menu = GUI::Menu::construct(); m_thickness_actions.set_exclusive(true); auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create(String::number(size), [this, size](auto& action) { + auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { m_thickness = size; - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); m_thickness_actions.add_action(*action); m_context_menu->add_action(move(action)); diff --git a/Applications/PaintBrush/PenTool.cpp b/Applications/PaintBrush/PenTool.cpp index 1cfcf4cde29..05f1c602c74 100644 --- a/Applications/PaintBrush/PenTool.cpp +++ b/Applications/PaintBrush/PenTool.cpp @@ -79,11 +79,9 @@ void PenTool::on_contextmenu(GUI::ContextMenuEvent& event) m_context_menu = GUI::Menu::construct(); m_thickness_actions.set_exclusive(true); auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create(String::number(size), [this, size](auto& action) { + auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { m_thickness = size; - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); m_thickness_actions.add_action(*action); m_context_menu->add_action(move(action)); diff --git a/Applications/PaintBrush/SprayTool.cpp b/Applications/PaintBrush/SprayTool.cpp index 2d56bd8d1d2..0f5031c03f0 100644 --- a/Applications/PaintBrush/SprayTool.cpp +++ b/Applications/PaintBrush/SprayTool.cpp @@ -105,11 +105,9 @@ void SprayTool::on_contextmenu(GUI::ContextMenuEvent& event) m_context_menu = GUI::Menu::construct(); m_thickness_actions.set_exclusive(true); auto insert_action = [&](int size, bool checked = false) { - auto action = GUI::Action::create(String::number(size), [this, size](auto& action) { + auto action = GUI::Action::create_checkable(String::number(size), [this, size](auto&) { m_thickness = size; - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); m_thickness_actions.add_action(*action); m_context_menu->add_action(move(action)); diff --git a/Applications/SoundPlayer/main.cpp b/Applications/SoundPlayer/main.cpp index c2ad025f341..e5477d02961 100644 --- a/Applications/SoundPlayer/main.cpp +++ b/Applications/SoundPlayer/main.cpp @@ -74,11 +74,9 @@ int main(int argc, char** argv) player.manager().play(); } - auto hide_scope = GUI::Action::create("Hide scope", { Mod_Ctrl, Key_H }, [&](GUI::Action& action) { - action.set_checked(!action.is_checked()); + auto hide_scope = GUI::Action::create_checkable("Hide scope", { Mod_Ctrl, Key_H }, [&](auto& action) { player.hide_scope(action.is_checked()); }); - hide_scope->set_checkable(true); app_menu.add_action(GUI::CommonActions::make_open_action([&](auto&) { Optional path = GUI::FilePicker::get_open_filepath("Open wav file..."); diff --git a/Applications/SystemMonitor/main.cpp b/Applications/SystemMonitor/main.cpp index eefd7a49803..ca2a27afce2 100644 --- a/Applications/SystemMonitor/main.cpp +++ b/Applications/SystemMonitor/main.cpp @@ -200,11 +200,9 @@ int main(int argc, char** argv) frequency_action_group.set_exclusive(true); auto make_frequency_action = [&](auto& title, int interval, bool checked = false) { - auto action = GUI::Action::create(title, [&refresh_timer, interval](auto& action) { + auto action = GUI::Action::create_checkable(title, [&refresh_timer, interval](auto&) { refresh_timer.restart(interval); - action.set_checked(true); }); - action->set_checkable(true); action->set_checked(checked); frequency_action_group.add_action(*action); frequency_menu.add_action(*action); diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp index a89a5c3a797..1fda2345c12 100644 --- a/Applications/Terminal/main.cpp +++ b/Applications/Terminal/main.cpp @@ -278,8 +278,7 @@ int main(int argc, char** argv) font_action_group.set_exclusive(true); auto& font_menu = menubar->add_menu("Font"); GUI::FontDatabase::the().for_each_fixed_width_font([&](const StringView& font_name) { - auto action = GUI::Action::create(font_name, [&](GUI::Action& action) { - action.set_checked(true); + auto action = GUI::Action::create_checkable(font_name, [&](auto& action) { terminal.set_font(GUI::FontDatabase::the().get_by_name(action.text())); auto metadata = GUI::FontDatabase::the().get_metadata_by_name(action.text()); ASSERT(metadata.has_value()); @@ -288,7 +287,6 @@ int main(int argc, char** argv) terminal.force_repaint(); }); font_action_group.add_action(*action); - action->set_checkable(true); if (terminal.font().name() == font_name) action->set_checked(true); font_menu.add_action(*action); diff --git a/Applications/TextEditor/TextEditorWidget.cpp b/Applications/TextEditor/TextEditorWidget.cpp index bf1ce9d8645..d824b9cf2b2 100644 --- a/Applications/TextEditor/TextEditorWidget.cpp +++ b/Applications/TextEditor/TextEditorWidget.cpp @@ -334,11 +334,9 @@ TextEditorWidget::TextEditorWidget() m_save_as_action->activate(); }); - m_line_wrapping_setting_action = GUI::Action::create("Line wrapping", [&](GUI::Action& action) { - action.set_checked(!action.is_checked()); + m_line_wrapping_setting_action = GUI::Action::create_checkable("Line wrapping", [&](auto& action) { m_editor->set_line_wrapping_enabled(action.is_checked()); }); - m_line_wrapping_setting_action->set_checkable(true); m_line_wrapping_setting_action->set_checked(m_editor->is_line_wrapping_enabled()); auto menubar = GUI::MenuBar::construct(); @@ -382,31 +380,25 @@ TextEditorWidget::TextEditorWidget() syntax_actions.set_exclusive(true); auto& syntax_menu = menubar->add_menu("Syntax"); - m_plain_text_highlight = GUI::Action::create("Plain Text", [&](GUI::Action& action) { - action.set_checked(true); + m_plain_text_highlight = GUI::Action::create_checkable("Plain text", [&](auto&) { m_editor->set_syntax_highlighter(nullptr); m_editor->update(); }); - m_plain_text_highlight->set_checkable(true); m_plain_text_highlight->set_checked(true); syntax_actions.add_action(*m_plain_text_highlight); syntax_menu.add_action(*m_plain_text_highlight); - m_cpp_highlight = GUI::Action::create("C++", [&](GUI::Action& action) { - action.set_checked(true); + m_cpp_highlight = GUI::Action::create_checkable("C++", [&](auto&) { m_editor->set_syntax_highlighter(make()); m_editor->update(); }); - m_cpp_highlight->set_checkable(true); syntax_actions.add_action(*m_cpp_highlight); syntax_menu.add_action(*m_cpp_highlight); - m_js_highlight = GUI::Action::create("Javascript", [&](GUI::Action& action) { - action.set_checked(true); + m_js_highlight = GUI::Action::create_checkable("JavaScript", [&](auto&) { m_editor->set_syntax_highlighter(make()); m_editor->update(); }); - m_js_highlight->set_checkable(true); syntax_actions.add_action(*m_js_highlight); syntax_menu.add_action(*m_js_highlight); diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index 0edabf04cf3..372886a1612 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -274,10 +274,9 @@ int main(int argc, char** argv) GUI::ActionGroup tool_actions; tool_actions.set_exclusive(true); - auto cursor_tool_action = GUI::Action::create("Cursor", Gfx::Bitmap::load_from_file("/res/icons/widgets/Cursor.png"), [&](auto&) { + auto cursor_tool_action = GUI::Action::create_checkable("Cursor", Gfx::Bitmap::load_from_file("/res/icons/widgets/Cursor.png"), [&](auto&) { g_form_editor_widget->set_tool(make(*g_form_editor_widget)); }); - cursor_tool_action->set_checkable(true); cursor_tool_action->set_checked(true); tool_actions.add_action(cursor_tool_action); @@ -285,14 +284,13 @@ int main(int argc, char** argv) GUI::WidgetClassRegistration::for_each([&](const GUI::WidgetClassRegistration& reg) { auto icon_path = String::format("/res/icons/widgets/G%s.png", reg.class_name().characters()); - auto action = GUI::Action::create(reg.class_name(), Gfx::Bitmap::load_from_file(icon_path), [®](auto&) { + auto action = GUI::Action::create_checkable(reg.class_name(), Gfx::Bitmap::load_from_file(icon_path), [®](auto&) { g_form_editor_widget->set_tool(make(*g_form_editor_widget, reg)); auto widget = reg.construct(); g_form_editor_widget->form_widget().add_child(widget); widget->set_relative_rect(30, 30, 30, 30); g_form_editor_widget->model().update(); }); - action->set_checkable(true); action->set_checked(false); tool_actions.add_action(action); form_widgets_toolbar.add_action(move(action)); diff --git a/DevTools/ProfileViewer/main.cpp b/DevTools/ProfileViewer/main.cpp index 325f3dfad9f..193e52173bd 100644 --- a/DevTools/ProfileViewer/main.cpp +++ b/DevTools/ProfileViewer/main.cpp @@ -85,21 +85,17 @@ int main(int argc, char** argv) app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { app.quit(); })); auto& view_menu = menubar->add_menu("View"); - auto invert_action = GUI::Action::create("Invert tree", { Mod_Ctrl, Key_I }, [&](auto& action) { - action.set_checked(!action.is_checked()); + auto invert_action = GUI::Action::create_checkable("Invert tree", { Mod_Ctrl, Key_I }, [&](auto& action) { profile->set_inverted(action.is_checked()); }); - invert_action->set_checkable(true); invert_action->set_checked(false); view_menu.add_action(invert_action); - auto percent_action = GUI::Action::create("Show percentages", { Mod_Ctrl, Key_P }, [&](auto& action) { - action.set_checked(!action.is_checked()); + auto percent_action = GUI::Action::create_checkable("Show percentages", { Mod_Ctrl, Key_P }, [&](auto& action) { profile->set_show_percentages(action.is_checked()); tree_view.update(); disassembly_view.update(); }); - percent_action->set_checkable(true); percent_action->set_checked(false); view_menu.add_action(percent_action); diff --git a/Games/Minesweeper/main.cpp b/Games/Minesweeper/main.cpp index 72fe4bbab5d..7f9a3808924 100644 --- a/Games/Minesweeper/main.cpp +++ b/Games/Minesweeper/main.cpp @@ -90,12 +90,9 @@ int main(int argc, char** argv) app_menu.add_separator(); - NonnullRefPtr chord_toggler_action = GUI::Action::create("Single-click chording", [&](const GUI::Action&) { - bool toggled = !field.is_single_chording(); - field.set_single_chording(toggled); - chord_toggler_action->set_checked(toggled); + auto chord_toggler_action = GUI::Action::create_checkable("Single-click chording", [&](auto& action) { + field.set_single_chording(!action.is_checked()); }); - chord_toggler_action->set_checkable(true); chord_toggler_action->set_checked(field.is_single_chording()); app_menu.add_action(*chord_toggler_action); diff --git a/Libraries/LibGUI/AbstractTableView.cpp b/Libraries/LibGUI/AbstractTableView.cpp index af35484c859..705d94c0aa1 100644 --- a/Libraries/LibGUI/AbstractTableView.cpp +++ b/Libraries/LibGUI/AbstractTableView.cpp @@ -199,11 +199,9 @@ Menu& AbstractTableView::ensure_header_context_menu() for (int column = 0; column < model()->column_count(); ++column) { auto& column_data = this->column_data(column); auto name = model()->column_name(column); - column_data.visibility_action = Action::create(name, [this, column](Action& action) { - action.set_checked(!action.is_checked()); + column_data.visibility_action = Action::create_checkable(name, [this, column](auto& action) { set_column_hidden(column, !action.is_checked()); }); - column_data.visibility_action->set_checkable(true); column_data.visibility_action->set_checked(true); m_header_context_menu->add_action(*column_data.visibility_action); diff --git a/Libraries/LibGUI/Action.cpp b/Libraries/LibGUI/Action.cpp index 03f136642f6..036a0712b8c 100644 --- a/Libraries/LibGUI/Action.cpp +++ b/Libraries/LibGUI/Action.cpp @@ -34,109 +34,112 @@ namespace GUI { namespace CommonActions { - NonnullRefPtr make_open_action(Function callback, Core::Object* parent) - { - return Action::create("Open...", { Mod_Ctrl, Key_O }, Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"), move(callback), parent); - } +NonnullRefPtr make_open_action(Function callback, Core::Object* parent) +{ + return Action::create("Open...", { Mod_Ctrl, Key_O }, Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png"), move(callback), parent); +} - NonnullRefPtr make_move_to_front_action(Function callback, Core::Object* parent) - { - return Action::create("Move to front", { Mod_Ctrl | Mod_Shift, Key_Up }, Gfx::Bitmap::load_from_file("/res/icons/16x16/move-to-front.png"), move(callback), parent); - } +NonnullRefPtr make_move_to_front_action(Function callback, Core::Object* parent) +{ + return Action::create("Move to front", { Mod_Ctrl | Mod_Shift, Key_Up }, Gfx::Bitmap::load_from_file("/res/icons/16x16/move-to-front.png"), move(callback), parent); +} - NonnullRefPtr make_move_to_back_action(Function callback, Core::Object* parent) - { - return Action::create("Move to back", { Mod_Ctrl | Mod_Shift, Key_Down }, Gfx::Bitmap::load_from_file("/res/icons/16x16/move-to-back.png"), move(callback), parent); - } +NonnullRefPtr make_move_to_back_action(Function callback, Core::Object* parent) +{ + return Action::create("Move to back", { Mod_Ctrl | Mod_Shift, Key_Down }, Gfx::Bitmap::load_from_file("/res/icons/16x16/move-to-back.png"), move(callback), parent); +} - NonnullRefPtr make_undo_action(Function callback, Core::Object* parent) - { - return Action::create("Undo", { Mod_Ctrl, Key_Z }, Gfx::Bitmap::load_from_file("/res/icons/16x16/undo.png"), move(callback), parent); - } +NonnullRefPtr make_undo_action(Function callback, Core::Object* parent) +{ + return Action::create("Undo", { Mod_Ctrl, Key_Z }, Gfx::Bitmap::load_from_file("/res/icons/16x16/undo.png"), move(callback), parent); +} - NonnullRefPtr make_redo_action(Function callback, Core::Object* parent) - { - return Action::create("Redo", { Mod_Ctrl, Key_Y }, Gfx::Bitmap::load_from_file("/res/icons/16x16/redo.png"), move(callback), parent); - } +NonnullRefPtr make_redo_action(Function callback, Core::Object* parent) +{ + return Action::create("Redo", { Mod_Ctrl, Key_Y }, Gfx::Bitmap::load_from_file("/res/icons/16x16/redo.png"), move(callback), parent); +} - NonnullRefPtr make_delete_action(Function callback, Core::Object* parent) - { - return Action::create("Delete", { Mod_None, Key_Delete }, Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"), move(callback), parent); - } +NonnullRefPtr make_delete_action(Function callback, Core::Object* parent) +{ + return Action::create("Delete", { Mod_None, Key_Delete }, Gfx::Bitmap::load_from_file("/res/icons/16x16/delete.png"), move(callback), parent); +} - NonnullRefPtr make_cut_action(Function callback, Core::Object* parent) - { - return Action::create("Cut", { Mod_Ctrl, Key_X }, Gfx::Bitmap::load_from_file("/res/icons/cut16.png"), move(callback), parent); - } +NonnullRefPtr make_cut_action(Function callback, Core::Object* parent) +{ + return Action::create("Cut", { Mod_Ctrl, Key_X }, Gfx::Bitmap::load_from_file("/res/icons/cut16.png"), move(callback), parent); +} - NonnullRefPtr make_copy_action(Function callback, Core::Object* parent) - { - return Action::create("Copy", { Mod_Ctrl, Key_C }, Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"), move(callback), parent); - } +NonnullRefPtr make_copy_action(Function callback, Core::Object* parent) +{ + return Action::create("Copy", { Mod_Ctrl, Key_C }, Gfx::Bitmap::load_from_file("/res/icons/16x16/edit-copy.png"), move(callback), parent); +} - NonnullRefPtr make_paste_action(Function callback, Core::Object* parent) - { - return Action::create("Paste", { Mod_Ctrl, Key_V }, Gfx::Bitmap::load_from_file("/res/icons/paste16.png"), move(callback), parent); - } +NonnullRefPtr make_paste_action(Function callback, Core::Object* parent) +{ + return Action::create("Paste", { Mod_Ctrl, Key_V }, Gfx::Bitmap::load_from_file("/res/icons/paste16.png"), move(callback), parent); +} - NonnullRefPtr make_fullscreen_action(Function callback, Core::Object* parent) - { - return Action::create("Fullscreen", { Mod_None, Key_F11 }, move(callback), parent); - } +NonnullRefPtr make_fullscreen_action(Function callback, Core::Object* parent) +{ + return Action::create("Fullscreen", { Mod_None, Key_F11 }, move(callback), parent); +} - NonnullRefPtr make_quit_action(Function callback) - { - return Action::create("Quit", { Mod_Alt, Key_F4 }, move(callback)); - } +NonnullRefPtr make_quit_action(Function callback) +{ + return Action::create("Quit", { Mod_Alt, Key_F4 }, move(callback)); +} - NonnullRefPtr make_go_back_action(Function callback, Core::Object* parent) - { - return Action::create("Go back", { Mod_Alt, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), move(callback), parent); - } +NonnullRefPtr make_go_back_action(Function callback, Core::Object* parent) +{ + return Action::create("Go back", { Mod_Alt, Key_Left }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"), move(callback), parent); +} - NonnullRefPtr make_go_forward_action(Function callback, Core::Object* parent) - { - return Action::create("Go forward", { Mod_Alt, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), move(callback), parent); - } +NonnullRefPtr make_go_forward_action(Function callback, Core::Object* parent) +{ + return Action::create("Go forward", { Mod_Alt, Key_Right }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"), move(callback), parent); +} - NonnullRefPtr make_go_home_action(Function callback, Core::Object* parent) - { - return Action::create("Go home", { Mod_Alt, Key_Home }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-home.png"), move(callback), parent); - } +NonnullRefPtr make_go_home_action(Function callback, Core::Object* parent) +{ + return Action::create("Go home", { Mod_Alt, Key_Home }, Gfx::Bitmap::load_from_file("/res/icons/16x16/go-home.png"), move(callback), parent); +} - NonnullRefPtr make_reload_action(Function callback, Core::Object* parent) - { - return Action::create("Reload", { Mod_Ctrl, Key_R }, Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"), move(callback), parent); - } +NonnullRefPtr make_reload_action(Function callback, Core::Object* parent) +{ + return Action::create("Reload", { Mod_Ctrl, Key_R }, Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"), move(callback), parent); +} } -Action::Action(const StringView& text, Function on_activation_callback, Core::Object* parent) +Action::Action(const StringView& text, Function on_activation_callback, Core::Object* parent, bool checkable) : Core::Object(parent) , on_activation(move(on_activation_callback)) , m_text(text) + , m_checkable(checkable) { } -Action::Action(const StringView& text, RefPtr&& icon, Function on_activation_callback, Core::Object* parent) +Action::Action(const StringView& text, RefPtr&& icon, Function on_activation_callback, Core::Object* parent, bool checkable) : Core::Object(parent) , on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) + , m_checkable(checkable) { } -Action::Action(const StringView& text, const Shortcut& shortcut, Function on_activation_callback, Core::Object* parent) - : Action(text, shortcut, nullptr, move(on_activation_callback), parent) +Action::Action(const StringView& text, const Shortcut& shortcut, Function on_activation_callback, Core::Object* parent, bool checkable) + : Action(text, shortcut, nullptr, move(on_activation_callback), parent, checkable) { } -Action::Action(const StringView& text, const Shortcut& shortcut, RefPtr&& icon, Function on_activation_callback, Core::Object* parent) +Action::Action(const StringView& text, const Shortcut& shortcut, RefPtr&& icon, Function on_activation_callback, Core::Object* parent, bool checkable) : Core::Object(parent) , on_activation(move(on_activation_callback)) , m_text(text) , m_icon(move(icon)) , m_shortcut(shortcut) + , m_checkable(checkable) { if (parent && Core::is(*parent)) { m_scope = ShortcutScope::WidgetLocal; @@ -156,10 +159,24 @@ Action::~Action() void Action::activate(Core::Object* activator) { + if (!on_activation) + return; + if (activator) m_activator = activator->make_weak_ptr(); - if (on_activation) - on_activation(*this); + + if (is_checkable()) { + if (m_action_group) { + if (m_action_group->is_unchecking_allowed()) + set_checked(!is_checked()); + else + set_checked(true); + } else { + set_checked(!is_checked()); + } + } + + on_activation(*this); m_activator = nullptr; } diff --git a/Libraries/LibGUI/Action.h b/Libraries/LibGUI/Action.h index 5064d74d00c..7197cae669e 100644 --- a/Libraries/LibGUI/Action.h +++ b/Libraries/LibGUI/Action.h @@ -84,6 +84,23 @@ public: { return adopt(*new Action(text, shortcut, move(icon), move(callback), parent)); } + static NonnullRefPtr create_checkable(const StringView& text, Function callback, Core::Object* parent = nullptr) + { + return adopt(*new Action(text, move(callback), parent, true)); + } + static NonnullRefPtr create_checkable(const StringView& text, RefPtr&& icon, Function callback, Core::Object* parent = nullptr) + { + return adopt(*new Action(text, move(icon), move(callback), parent, true)); + } + static NonnullRefPtr create_checkable(const StringView& text, const Shortcut& shortcut, Function callback, Core::Object* parent = nullptr) + { + return adopt(*new Action(text, shortcut, move(callback), parent, true)); + } + static NonnullRefPtr create_checkable(const StringView& text, const Shortcut& shortcut, RefPtr&& icon, Function callback, Core::Object* parent = nullptr) + { + return adopt(*new Action(text, shortcut, move(icon), move(callback), parent, true)); + } + virtual ~Action() override; String text() const { return m_text; } @@ -122,10 +139,10 @@ public: private: virtual bool is_action() const override { return true; } - Action(const StringView& text, Function = nullptr, Core::Object* = nullptr); - Action(const StringView& text, const Shortcut&, Function = nullptr, Core::Object* = nullptr); - Action(const StringView& text, const Shortcut&, RefPtr&& icon, Function = nullptr, Core::Object* = nullptr); - Action(const StringView& text, RefPtr&& icon, Function = nullptr, Core::Object* = nullptr); + Action(const StringView& text, Function = nullptr, Core::Object* = nullptr, bool checkable = false); + Action(const StringView& text, const Shortcut&, Function = nullptr, Core::Object* = nullptr, bool checkable = false); + Action(const StringView& text, const Shortcut&, RefPtr&& icon, Function = nullptr, Core::Object* = nullptr, bool checkable = false); + Action(const StringView& text, RefPtr&& icon, Function = nullptr, Core::Object* = nullptr, bool checkable = false); template void for_each_toolbar_button(Callback); diff --git a/Libraries/LibGUI/MultiView.cpp b/Libraries/LibGUI/MultiView.cpp index 27256da9690..9b542eb67f2 100644 --- a/Libraries/LibGUI/MultiView.cpp +++ b/Libraries/LibGUI/MultiView.cpp @@ -165,27 +165,21 @@ void MultiView::set_column_hidden(int column_index, bool hidden) void MultiView::build_actions() { - m_view_as_table_action = Action::create( + m_view_as_table_action = Action::create_checkable( "Table view", Gfx::Bitmap::load_from_file("/res/icons/16x16/table-view.png"), [this](auto&) { set_view_mode(ViewMode::List); - m_view_as_table_action->set_checked(true); }); - m_view_as_table_action->set_checkable(true); - m_view_as_icons_action = Action::create( + m_view_as_icons_action = Action::create_checkable( "Icon view", Gfx::Bitmap::load_from_file("/res/icons/16x16/icon-view.png"), [this](auto&) { set_view_mode(ViewMode::Icon); - m_view_as_icons_action->set_checked(true); }); - m_view_as_icons_action->set_checkable(true); #ifdef MULTIVIEW_WITH_COLUMNSVIEW - m_view_as_columns_action = Action::create( + m_view_as_columns_action = Action::create_checkable( "Columns view", Gfx::Bitmap::load_from_file("/res/icons/16x16/columns-view.png"), [this](auto&) { set_view_mode(ViewMode::Columns); - m_view_as_columns_action->set_checked(true); }); - m_view_as_columns_action->set_checkable(true); #endif m_view_type_action_group = make();