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 06a88877ea..1ed5d03d3b 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 c37b8d3deb..d0c1044c14 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 0824d72b3d..b0f5f80b5a 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 4a6326d525..6b21a36116 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 8984f52ccd..f5efb126d0 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 1cfcf4cde2..05f1c602c7 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 2d56bd8d1d..0f5031c03f 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 c2ad025f34..e5477d0296 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 eefd7a4980..ca2a27afce 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 a89a5c3a79..1fda2345c1 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 bf1ce9d864..d824b9cf2b 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 0edabf04cf..372886a161 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 325f3dfad9..193e52173b 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 72fe4bbab5..7f9a380892 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 af35484c85..705d94c0aa 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 03f136642f..036a0712b8 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 5064d74d00..7197cae669 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 27256da969..9b542eb67f 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();