1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:38:11 +00:00

LibGUI: Add GActionGroup, a way to group a bunch of GActions.

This can be used to make a bunch of actions mutually exclusive.
This patch only implements the exclusivity behavior for buttons.
This commit is contained in:
Andreas Kling 2019-07-09 22:10:03 +02:00
parent 2ae0333f5d
commit 7083a0104a
9 changed files with 97 additions and 6 deletions

View file

@ -2,6 +2,7 @@
#include <AK/FileSystemPath.h> #include <AK/FileSystemPath.h>
#include <LibCore/CUserInfo.h> #include <LibCore/CUserInfo.h>
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GActionGroup.h>
#include <LibGUI/GApplication.h> #include <LibGUI/GApplication.h>
#include <LibGUI/GBoxLayout.h> #include <LibGUI/GBoxLayout.h>
#include <LibGUI/GFileSystemModel.h> #include <LibGUI/GFileSystemModel.h>
@ -108,18 +109,21 @@ int main(int argc, char** argv)
view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GAction&) { view_as_table_action = GAction::create("Table view", { Mod_Ctrl, KeyCode::Key_L }, GraphicsBitmap::load_from_file("/res/icons/16x16/table-view.png"), [&](const GAction&) {
directory_view->set_view_mode(DirectoryView::ViewMode::List); directory_view->set_view_mode(DirectoryView::ViewMode::List);
view_as_icons_action->set_checked(false);
view_as_table_action->set_checked(true); view_as_table_action->set_checked(true);
}); });
view_as_table_action->set_checkable(true); view_as_table_action->set_checkable(true);
view_as_table_action->set_checked(false);
view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GAction&) { view_as_icons_action = GAction::create("Icon view", { Mod_Ctrl, KeyCode::Key_I }, GraphicsBitmap::load_from_file("/res/icons/16x16/icon-view.png"), [&](const GAction&) {
directory_view->set_view_mode(DirectoryView::ViewMode::Icon); directory_view->set_view_mode(DirectoryView::ViewMode::Icon);
view_as_table_action->set_checked(false);
view_as_icons_action->set_checked(true); view_as_icons_action->set_checked(true);
}); });
view_as_icons_action->set_checkable(true); view_as_icons_action->set_checkable(true);
auto view_type_action_group = make<GActionGroup>();
view_type_action_group->set_exclusive(true);
view_type_action_group->add_action(*view_as_table_action);
view_type_action_group->add_action(*view_as_icons_action);
view_as_icons_action->set_checked(true); view_as_icons_action->set_checked(true);
auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [](const GAction&) { auto copy_action = GAction::create("Copy", GraphicsBitmap::load_from_file("/res/icons/16x16/edit-copy.png"), [](const GAction&) {

View file

@ -29,7 +29,8 @@ public:
virtual void click() = 0; virtual void click() = 0;
virtual const char* class_name() const override { return "GAbstractButton"; } virtual const char* class_name() const override { return "GAbstractButton"; }
virtual bool accepts_focus() const override { return true; } virtual bool accepts_focus() const override { return true; }
virtual bool supports_keyboard_activation() const { return true; } virtual bool supports_keyboard_activation() const override { return true; }
virtual bool is_uncheckable() const { return true; }
protected: protected:
explicit GAbstractButton(GWidget* parent); explicit GAbstractButton(GWidget* parent);

View file

@ -1,4 +1,5 @@
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GActionGroup.h>
#include <LibGUI/GApplication.h> #include <LibGUI/GApplication.h>
#include <LibGUI/GButton.h> #include <LibGUI/GButton.h>
#include <LibGUI/GMenuItem.h> #include <LibGUI/GMenuItem.h>
@ -105,6 +106,17 @@ void GAction::set_checked(bool checked)
if (m_checked == checked) if (m_checked == checked)
return; return;
m_checked = checked; m_checked = checked;
if (m_checked && m_action_group) {
m_action_group->for_each_action([this](auto& other_action) {
if (this == &other_action)
return IterationDecision::Continue;
if (other_action.is_checkable())
other_action.set_checked(false);
return IterationDecision::Continue;
});
}
for_each_toolbar_button([checked](GButton& button) { for_each_toolbar_button([checked](GButton& button) {
button.set_checked(checked); button.set_checked(checked);
}); });
@ -112,3 +124,8 @@ void GAction::set_checked(bool checked)
item.set_checked(checked); item.set_checked(checked);
}); });
} }
void GAction::set_group(Badge<GActionGroup>, GActionGroup* group)
{
m_action_group = group ? group->make_weak_ptr() : nullptr;
}

View file

@ -11,6 +11,7 @@
#include <LibGUI/GShortcut.h> #include <LibGUI/GShortcut.h>
#include <SharedGraphics/GraphicsBitmap.h> #include <SharedGraphics/GraphicsBitmap.h>
class GActionGroup;
class GButton; class GButton;
class GMenuItem; class GMenuItem;
class GWidget; class GWidget;
@ -70,6 +71,9 @@ public:
void register_menu_item(Badge<GMenuItem>, GMenuItem&); void register_menu_item(Badge<GMenuItem>, GMenuItem&);
void unregister_menu_item(Badge<GMenuItem>, GMenuItem&); void unregister_menu_item(Badge<GMenuItem>, GMenuItem&);
const GActionGroup* group() const { return m_action_group.ptr(); }
void set_group(Badge<GActionGroup>, GActionGroup*);
private: private:
GAction(const StringView& text, Function<void(GAction&)> = nullptr, GWidget* = nullptr); GAction(const StringView& text, Function<void(GAction&)> = nullptr, GWidget* = nullptr);
GAction(const StringView& text, const GShortcut&, Function<void(GAction&)> = nullptr, GWidget* = nullptr); GAction(const StringView& text, const GShortcut&, Function<void(GAction&)> = nullptr, GWidget* = nullptr);
@ -92,4 +96,5 @@ private:
HashTable<GButton*> m_buttons; HashTable<GButton*> m_buttons;
HashTable<GMenuItem*> m_menu_items; HashTable<GMenuItem*> m_menu_items;
WeakPtr<GWidget> m_widget; WeakPtr<GWidget> m_widget;
WeakPtr<GActionGroup> m_action_group;
}; };

View file

@ -0,0 +1,14 @@
#include <LibGUI/GAction.h>
#include <LibGUI/GActionGroup.h>
void GActionGroup::add_action(GAction& action)
{
action.set_group({}, this);
m_actions.set(&action);
}
void GActionGroup::remove_action(GAction& action)
{
action.set_group({}, nullptr);
m_actions.remove(&action);
}

View file

@ -0,0 +1,35 @@
#pragma once
#include <AK/HashTable.h>
#include <AK/Weakable.h>
class GAction;
class GActionGroup : public Weakable<GActionGroup> {
public:
GActionGroup() {}
~GActionGroup() {}
void add_action(GAction&);
void remove_action(GAction&);
bool is_exclusive() const { return m_exclusive; }
void set_exclusive(bool exclusive) { m_exclusive = exclusive; }
bool is_unchecking_allowed() const { return m_unchecking_allowed; }
void set_unchecking_allowed(bool unchecking_allowed) { m_unchecking_allowed = unchecking_allowed; }
template<typename C>
void for_each_action(C callback)
{
for (auto& it : m_actions) {
if (callback(*it) == IterationDecision::Break)
break;
}
}
private:
HashTable<GAction*> m_actions;
bool m_exclusive { false };
bool m_unchecking_allowed { false };
};

View file

@ -1,7 +1,8 @@
#include "GButton.h"
#include <AK/StringBuilder.h> #include <AK/StringBuilder.h>
#include <Kernel/KeyCode.h> #include <Kernel/KeyCode.h>
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GActionGroup.h>
#include <LibGUI/GButton.h>
#include <LibGUI/GPainter.h> #include <LibGUI/GPainter.h>
#include <SharedGraphics/StylePainter.h> #include <SharedGraphics/StylePainter.h>
@ -60,8 +61,11 @@ void GButton::click()
{ {
if (!is_enabled()) if (!is_enabled())
return; return;
if (is_checkable()) if (is_checkable()) {
if (is_checked() && !is_uncheckable())
return;
set_checked(!is_checked()); set_checked(!is_checked());
}
if (on_click) if (on_click)
on_click(*this); on_click(*this);
} }
@ -88,3 +92,12 @@ void GButton::set_icon(RefPtr<GraphicsBitmap>&& icon)
m_icon = move(icon); m_icon = move(icon);
update(); update();
} }
bool GButton::is_uncheckable() const
{
if (!m_action)
return true;
if (!m_action->group())
return true;
return m_action->group()->is_unchecking_allowed();
}

View file

@ -34,6 +34,7 @@ public:
virtual const char* class_name() const override { return "GButton"; } virtual const char* class_name() const override { return "GButton"; }
virtual bool accepts_focus() const override { return m_focusable; } virtual bool accepts_focus() const override { return m_focusable; }
virtual bool supports_keyboard_activation() const override; virtual bool supports_keyboard_activation() const override;
virtual bool is_uncheckable() const override;
void set_focusable(bool b) { m_focusable = b; } void set_focusable(bool b) { m_focusable = b; }

View file

@ -27,6 +27,7 @@ LIBGUI_OBJS = \
GMenuItem.o \ GMenuItem.o \
GApplication.o \ GApplication.o \
GAction.o \ GAction.o \
GActionGroup.o \
GFontDatabase.o \ GFontDatabase.o \
GToolBar.o \ GToolBar.o \
GTableView.o \ GTableView.o \