diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-04-12 02:53:27 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-04-12 02:53:27 +0200 |
commit | 054c9821819de22d1522021978959e50f8fc6382 (patch) | |
tree | 7673e6d859fe6cfb26c28d944724ef8d53f0be99 | |
parent | 32e5c8c6890a27acb268cd43f087731e11f99d79 (diff) | |
download | serenity-054c9821819de22d1522021978959e50f8fc6382.zip |
LibGUI+WindowServer: Add support for enabled/disabled actions.
The enabled state of a GAction now propagates both to any toolbar buttons
and any menu items linked to the action. Toolbar buttons are painted in
a grayed out style when disabled. Menu items are gray when disabled. :^)
-rw-r--r-- | Applications/TextEditor/main.cpp | 11 | ||||
-rw-r--r-- | LibGUI/GAction.cpp | 49 | ||||
-rw-r--r-- | LibGUI/GAction.h | 23 | ||||
-rw-r--r-- | LibGUI/GButton.cpp | 50 | ||||
-rw-r--r-- | LibGUI/GButton.h | 5 | ||||
-rw-r--r-- | LibGUI/GEventLoop.cpp | 10 | ||||
-rw-r--r-- | LibGUI/GMenu.cpp | 7 | ||||
-rw-r--r-- | LibGUI/GMenuItem.cpp | 43 | ||||
-rw-r--r-- | LibGUI/GMenuItem.h | 16 | ||||
-rw-r--r-- | LibGUI/GToolBar.cpp | 1 | ||||
-rw-r--r-- | Servers/WindowServer/WSAPITypes.h | 3 | ||||
-rw-r--r-- | Servers/WindowServer/WSClientConnection.cpp | 31 | ||||
-rw-r--r-- | Servers/WindowServer/WSClientConnection.h | 1 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenu.cpp | 17 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenu.h | 1 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenuItem.cpp | 20 | ||||
-rw-r--r-- | Servers/WindowServer/WSMenuItem.h | 14 | ||||
-rw-r--r-- | Servers/WindowServer/WSMessage.h | 32 | ||||
-rw-r--r-- | Servers/WindowServer/WSMessageLoop.cpp | 7 | ||||
-rw-r--r-- | Servers/WindowServer/WSWindowManager.cpp | 20 |
20 files changed, 308 insertions, 53 deletions
diff --git a/Applications/TextEditor/main.cpp b/Applications/TextEditor/main.cpp index d3aa6d38ec..be7a5c715b 100644 --- a/Applications/TextEditor/main.cpp +++ b/Applications/TextEditor/main.cpp @@ -128,16 +128,21 @@ int main(int argc, char** argv) toolbar->add_separator(); - toolbar->add_action(move(cut_action)); - toolbar->add_action(move(copy_action)); + toolbar->add_action(cut_action.copy_ref()); + toolbar->add_action(copy_action.copy_ref()); toolbar->add_action(move(paste_action)); - toolbar->add_action(move(delete_action)); + toolbar->add_action(delete_action.copy_ref()); toolbar->add_separator(); toolbar->add_action(move(undo_action)); toolbar->add_action(move(redo_action)); + text_editor->on_selection_change = [&] { + cut_action->set_enabled(text_editor->has_selection()); + copy_action->set_enabled(text_editor->has_selection()); + }; + auto* window = new GWindow; window->set_title(String::format("TextEditor: %s", path.characters())); window->set_rect(20, 200, 640, 400); diff --git a/LibGUI/GAction.cpp b/LibGUI/GAction.cpp index 5a0106530d..c183340e0e 100644 --- a/LibGUI/GAction.cpp +++ b/LibGUI/GAction.cpp @@ -1,5 +1,7 @@ #include <LibGUI/GAction.h> #include <LibGUI/GApplication.h> +#include <LibGUI/GButton.h> +#include <LibGUI/GMenuItem.h> GAction::GAction(const String& text, const String& custom_data, Function<void(const GAction&)> on_activation_callback) : on_activation(move(on_activation_callback)) @@ -46,3 +48,50 @@ void GAction::activate() if (on_activation) on_activation(*this); } + +void GAction::register_button(Badge<GButton>, GButton& button) +{ + m_buttons.set(&button); +} + +void GAction::unregister_button(Badge<GButton>, GButton& button) +{ + m_buttons.remove(&button); +} + +void GAction::register_menu_item(Badge<GMenuItem>, GMenuItem& menu_item) +{ + m_menu_items.set(&menu_item); +} + +void GAction::unregister_menu_item(Badge<GMenuItem>, GMenuItem& menu_item) +{ + m_menu_items.remove(&menu_item); +} + +template<typename Callback> +void GAction::for_each_toolbar_button(Callback callback) +{ + for (auto& it : m_buttons) + callback(*it); +} + +template<typename Callback> +void GAction::for_each_menu_item(Callback callback) +{ + for (auto& it : m_menu_items) + callback(*it); +} + +void GAction::set_enabled(bool enabled) +{ + if (m_enabled == enabled) + return; + m_enabled = enabled; + for_each_toolbar_button([enabled] (GButton& button) { + button.set_enabled(enabled); + }); + for_each_menu_item([enabled] (GMenuItem& item) { + item.set_enabled(enabled); + }); +} diff --git a/LibGUI/GAction.h b/LibGUI/GAction.h index f85cda11f0..f691aeed50 100644 --- a/LibGUI/GAction.h +++ b/LibGUI/GAction.h @@ -4,10 +4,16 @@ #include <AK/Function.h> #include <AK/Retainable.h> #include <AK/Retained.h> +#include <AK/Weakable.h> +#include <AK/Badge.h> +#include <AK/HashTable.h> #include <SharedGraphics/GraphicsBitmap.h> #include <LibGUI/GShortcut.h> -class GAction : public Retainable<GAction> { +class GButton; +class GMenuItem; + +class GAction : public Retainable<GAction>, public Weakable<GAction> { public: static Retained<GAction> create(const String& text, Function<void(const GAction&)> callback) { @@ -40,6 +46,14 @@ public: void activate(); + bool is_enabled() const { return m_enabled; } + void set_enabled(bool); + + void register_button(Badge<GButton>, GButton&); + void unregister_button(Badge<GButton>, GButton&); + void register_menu_item(Badge<GMenuItem>, GMenuItem&); + void unregister_menu_item(Badge<GMenuItem>, GMenuItem&); + private: GAction(const String& text, Function<void(const GAction&)> = nullptr); GAction(const String& text, const GShortcut&, Function<void(const GAction&)> = nullptr); @@ -47,9 +61,16 @@ private: GAction(const String& text, RetainPtr<GraphicsBitmap>&& icon, Function<void(const GAction&)> = nullptr); GAction(const String& text, const String& custom_data = String(), Function<void(const GAction&)> = nullptr); + template<typename Callback> void for_each_toolbar_button(Callback); + template<typename Callback> void for_each_menu_item(Callback); + String m_text; String m_custom_data; RetainPtr<GraphicsBitmap> m_icon; GShortcut m_shortcut; + bool m_enabled { true }; + + HashTable<GButton*> m_buttons; + HashTable<GMenuItem*> m_menu_items; }; diff --git a/LibGUI/GButton.cpp b/LibGUI/GButton.cpp index 7496042fed..b78f0358fd 100644 --- a/LibGUI/GButton.cpp +++ b/LibGUI/GButton.cpp @@ -2,6 +2,7 @@ #include <LibGUI/GPainter.h> #include <SharedGraphics/StylePainter.h> #include <AK/StringBuilder.h> +#include <LibGUI/GAction.h> //#define GBUTTON_DEBUG @@ -12,6 +13,8 @@ GButton::GButton(GWidget* parent) GButton::~GButton() { + if (m_action) + m_action->unregister_button({ }, *this); } void GButton::set_caption(const String& caption) @@ -35,7 +38,7 @@ void GButton::paint_event(GPaintEvent& event) GPainter painter(*this); painter.add_clip_rect(event.rect()); - StylePainter::paint_button(painter, rect(), m_button_style, m_being_pressed, m_hovered, m_checkable && m_checked); + StylePainter::paint_button(painter, rect(), m_button_style, m_being_pressed, m_hovered, m_checkable && m_checked, is_enabled()); if (m_caption.is_empty() && !m_icon) return; @@ -46,8 +49,12 @@ void GButton::paint_event(GPaintEvent& event) content_rect.move_by(1, 1); icon_location.move_by(1, 1); } - if (m_icon) - painter.blit(icon_location, *m_icon, m_icon->rect()); + if (m_icon) { + if (is_enabled()) + painter.blit(icon_location, *m_icon, m_icon->rect()); + else + painter.blit_dimmed(icon_location, *m_icon, m_icon->rect()); + } auto& font = (m_checkable && m_checked) ? Font::default_bold_font() : this->font(); painter.draw_text(content_rect, m_caption, font, text_alignment(), foreground_color(), TextElision::Right); } @@ -55,10 +62,12 @@ void GButton::paint_event(GPaintEvent& event) void GButton::mousemove_event(GMouseEvent& event) { if (event.buttons() == GMouseButton::Left) { - bool being_pressed = rect().contains(event.position()); - if (being_pressed != m_being_pressed) { - m_being_pressed = being_pressed; - update(); + if (is_enabled()) { + bool being_pressed = rect().contains(event.position()); + if (being_pressed != m_being_pressed) { + m_being_pressed = being_pressed; + update(); + } } } GWidget::mousemove_event(event); @@ -70,14 +79,18 @@ void GButton::mousedown_event(GMouseEvent& event) dbgprintf("GButton::mouse_down_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); #endif if (event.button() == GMouseButton::Left) { - m_being_pressed = true; - update(); + if (is_enabled()) { + m_being_pressed = true; + update(); + } } GWidget::mousedown_event(event); } void GButton::click() { + if (!is_enabled()) + return; if (on_click) on_click(*this); } @@ -88,11 +101,13 @@ void GButton::mouseup_event(GMouseEvent& event) dbgprintf("GButton::mouse_up_event: x=%d, y=%d, button=%u\n", event.x(), event.y(), (unsigned)event.button()); #endif if (event.button() == GMouseButton::Left) { - bool was_being_pressed = m_being_pressed; - m_being_pressed = false; - update(); - if (was_being_pressed) - click(); + if (is_enabled()) { + bool was_being_pressed = m_being_pressed; + m_being_pressed = false; + update(); + if (was_being_pressed) + click(); + } } GWidget::mouseup_event(event); } @@ -108,3 +123,10 @@ void GButton::leave_event(CEvent&) m_hovered = false; update(); } + +void GButton::set_action(GAction& action) +{ + m_action = action.make_weak_ptr(); + action.register_button({ }, *this); + set_enabled(action.is_enabled()); +} diff --git a/LibGUI/GButton.h b/LibGUI/GButton.h index b61ee2888a..a5f1d758f6 100644 --- a/LibGUI/GButton.h +++ b/LibGUI/GButton.h @@ -7,6 +7,8 @@ #include <SharedGraphics/GraphicsBitmap.h> #include <SharedGraphics/TextAlignment.h> +class GAction; + class GButton : public GWidget { public: explicit GButton(GWidget* parent); @@ -35,6 +37,8 @@ public: void click(); + void set_action(GAction&); + virtual const char* class_name() const override { return "GButton"; } private: @@ -49,6 +53,7 @@ private: RetainPtr<GraphicsBitmap> m_icon; ButtonStyle m_button_style { ButtonStyle::Normal }; TextAlignment m_text_alignment { TextAlignment::Center }; + WeakPtr<GAction> m_action; bool m_being_pressed { false }; bool m_hovered { false }; bool m_checkable { false }; diff --git a/LibGUI/GEventLoop.cpp b/LibGUI/GEventLoop.cpp index a7502c0b7e..11cac1ab92 100644 --- a/LibGUI/GEventLoop.cpp +++ b/LibGUI/GEventLoop.cpp @@ -22,7 +22,6 @@ //#define GEVENTLOOP_DEBUG //#define COALESCING_DEBUG -static HashMap<GShortcut, GAction*>* g_actions; int GEventLoop::s_event_fd = -1; pid_t GEventLoop::s_server_pid = -1; @@ -71,9 +70,6 @@ GEventLoop::GEventLoop() connected = true; } - if (!g_actions) - g_actions = new HashMap<GShortcut, GAction*>; - #ifdef GEVENTLOOP_DEBUG dbgprintf("(%u) GEventLoop constructed :)\n", getpid()); #endif @@ -125,8 +121,10 @@ void GEventLoop::handle_key_event(const WSAPI_ServerMessage& event, GWindow& win if (event.type == WSAPI_ServerMessage::Type::KeyDown) { if (auto* action = GApplication::the().action_for_key_event(*key_event)) { - action->activate(); - return; + if (action->is_enabled()) { + action->activate(); + return; + } } } post_event(window, move(key_event)); diff --git a/LibGUI/GMenu.cpp b/LibGUI/GMenu.cpp index 54712228ce..68feb1d0ce 100644 --- a/LibGUI/GMenu.cpp +++ b/LibGUI/GMenu.cpp @@ -31,12 +31,12 @@ GMenu::~GMenu() void GMenu::add_action(Retained<GAction>&& action) { - m_items.append(make<GMenuItem>(move(action))); + m_items.append(make<GMenuItem>(m_menu_id, move(action))); } void GMenu::add_separator() { - m_items.append(make<GMenuItem>(GMenuItem::Separator)); + m_items.append(make<GMenuItem>(m_menu_id, GMenuItem::Separator)); } int GMenu::realize_menu() @@ -52,6 +52,8 @@ int GMenu::realize_menu() ASSERT(m_menu_id > 0); for (int i = 0; i < m_items.size(); ++i) { auto& item = *m_items[i]; + item.set_menu_id({ }, m_menu_id); + item.set_identifier({ }, i); if (item.type() == GMenuItem::Separator) { WSAPI_ClientMessage request; request.type = WSAPI_ClientMessage::Type::AddMenuSeparator; @@ -65,6 +67,7 @@ int GMenu::realize_menu() request.type = WSAPI_ClientMessage::Type::AddMenuItem; request.menu.menu_id = m_menu_id; request.menu.identifier = i; + request.menu.enabled = action.is_enabled(); ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); strcpy(request.text, action.text().characters()); request.text_length = action.text().length(); diff --git a/LibGUI/GMenuItem.cpp b/LibGUI/GMenuItem.cpp index f15bb44c4f..8cbba6d943 100644 --- a/LibGUI/GMenuItem.cpp +++ b/LibGUI/GMenuItem.cpp @@ -1,18 +1,57 @@ #include <LibGUI/GMenuItem.h> #include <LibGUI/GAction.h> +#include <LibGUI/GEventLoop.h> +#include <WindowServer/WSAPITypes.h> -GMenuItem::GMenuItem(Type type) +GMenuItem::GMenuItem(unsigned menu_id, Type type) : m_type(type) + , m_menu_id(menu_id) { } -GMenuItem::GMenuItem(Retained<GAction>&& action) +GMenuItem::GMenuItem(unsigned menu_id, Retained<GAction>&& action) : m_type(Action) + , m_menu_id(menu_id) , m_action(move(action)) { + m_action->register_menu_item({ }, *this); + m_enabled = m_action->is_enabled(); } GMenuItem::~GMenuItem() { + if (m_action) + m_action->unregister_menu_item({ }, *this); } +void GMenuItem::set_enabled(bool enabled) +{ + if (m_enabled == enabled) + return; + m_enabled = enabled; + update_window_server(); +} + +void GMenuItem::update_window_server() +{ + auto& action = *m_action; + WSAPI_ClientMessage request; + request.type = WSAPI_ClientMessage::Type::UpdateMenuItem; + request.menu.menu_id = m_menu_id; + request.menu.identifier = m_identifier; + request.menu.enabled = action.is_enabled(); + ASSERT(action.text().length() < (ssize_t)sizeof(request.text)); + strcpy(request.text, action.text().characters()); + request.text_length = action.text().length(); + + if (action.shortcut().is_valid()) { + auto shortcut_text = action.shortcut().to_string(); + ASSERT(shortcut_text.length() < (ssize_t)sizeof(request.menu.shortcut_text)); + strcpy(request.menu.shortcut_text, shortcut_text.characters()); + request.menu.shortcut_text_length = shortcut_text.length(); + } else { + request.menu.shortcut_text_length = 0; + } + + GEventLoop::current().sync_request(request, WSAPI_ServerMessage::Type::DidUpdateMenuItem); +} diff --git a/LibGUI/GMenuItem.h b/LibGUI/GMenuItem.h index 9c391d9574..0cda499e2d 100644 --- a/LibGUI/GMenuItem.h +++ b/LibGUI/GMenuItem.h @@ -1,15 +1,17 @@ #pragma once #include <AK/AKString.h> +#include <AK/Badge.h> class GAction; +class GMenu; class GMenuItem { public: enum Type { Invalid, Action, Separator }; - explicit GMenuItem(Type); - explicit GMenuItem(Retained<GAction>&&); + GMenuItem(unsigned menu_id, Type); + GMenuItem(unsigned menu_id, Retained<GAction>&&); ~GMenuItem(); Type type() const { return m_type; } @@ -18,9 +20,19 @@ public: GAction* action() { return m_action.ptr(); } unsigned identifier() const { return m_identifier; } + bool is_enabled() const { return m_enabled; } + void set_enabled(bool); + + void set_menu_id(Badge<GMenu>, unsigned menu_id) { m_menu_id = menu_id; } + void set_identifier(Badge<GMenu>, unsigned identifier) { m_identifier = identifier; } + private: + void update_window_server(); + Type m_type { Invalid }; + unsigned m_menu_id { 0 }; unsigned m_identifier { 0 }; + bool m_enabled { true }; RetainPtr<GAction> m_action; }; diff --git a/LibGUI/GToolBar.cpp b/LibGUI/GToolBar.cpp index 11939e9cd8..5fe39f3c6a 100644 --- a/LibGUI/GToolBar.cpp +++ b/LibGUI/GToolBar.cpp @@ -26,6 +26,7 @@ void GToolBar::add_action(Retained<GAction>&& action) item->action = move(action); auto* button = new GButton(this); + button->set_action(*item->action); button->set_tooltip(item->action->text()); if (item->action->icon()) button->set_icon(item->action->icon()); diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index ef7e091fee..dd243454a5 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -82,6 +82,7 @@ struct WSAPI_ServerMessage { DidSetApplicationMenubar, DidAddMenuItem, DidAddMenuSeparator, + DidUpdateMenuItem, DidCreateWindow, DidDestroyWindow, DidGetWindowTitle, @@ -171,6 +172,7 @@ struct WSAPI_ClientMessage { SetApplicationMenubar, AddMenuItem, AddMenuSeparator, + UpdateMenuItem, CreateWindow, DestroyWindow, SetWindowTitle, @@ -211,6 +213,7 @@ struct WSAPI_ClientMessage { unsigned identifier; char shortcut_text[32]; int shortcut_text_length; + bool enabled; } menu; struct { WSAPI_Rect rect; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index 6937a3b59d..4231bd7be1 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -215,7 +215,7 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request) return; } auto& menu = *(*it).value; - menu.add_item(make<WSMenuItem>(identifier, request.text(), request.shortcut_text())); + menu.add_item(make<WSMenuItem>(menu, identifier, request.text(), request.shortcut_text(), request.is_enabled())); WSAPI_ServerMessage response; response.type = WSAPI_ServerMessage::Type::DidAddMenuItem; response.menu.menu_id = menu_id; @@ -223,6 +223,31 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request) post_message(response); } +void WSClientConnection::handle_request(const WSAPIUpdateMenuItemRequest& request) +{ + int menu_id = request.menu_id(); + unsigned identifier = request.identifier(); + auto it = m_menus.find(menu_id); + if (it == m_menus.end()) { + post_error("WSAPIUpdateMenuItemRequest: Bad menu ID"); + return; + } + auto& menu = *(*it).value; + auto* menu_item = menu.item_with_identifier(request.identifier()); + if (!menu_item) { + post_error("WSAPIUpdateMenuItemRequest: Bad menu item identifier"); + return; + } + menu_item->set_text(request.text()); + menu_item->set_shortcut_text(request.shortcut_text()); + menu_item->set_enabled(request.is_enabled()); + WSAPI_ServerMessage response; + response.type = WSAPI_ServerMessage::Type::DidUpdateMenuItem; + response.menu.menu_id = menu_id; + response.menu.identifier = identifier; + post_message(response); +} + void WSClientConnection::handle_request(const WSAPIAddMenuSeparatorRequest& request) { int menu_id = request.menu_id(); @@ -232,7 +257,7 @@ void WSClientConnection::handle_request(const WSAPIAddMenuSeparatorRequest& requ return; } auto& menu = *(*it).value; - menu.add_item(make<WSMenuItem>(WSMenuItem::Separator)); + menu.add_item(make<WSMenuItem>(menu, WSMenuItem::Separator)); WSAPI_ServerMessage response; response.type = WSAPI_ServerMessage::Type::DidAddMenuSeparator; response.menu.menu_id = menu_id; @@ -553,6 +578,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request) return handle_request(static_cast<const WSAPIAddMenuItemRequest&>(request)); case WSMessage::APIAddMenuSeparatorRequest: return handle_request(static_cast<const WSAPIAddMenuSeparatorRequest&>(request)); + case WSMessage::APIUpdateMenuItemRequest: + return handle_request(static_cast<const WSAPIUpdateMenuItemRequest&>(request)); case WSMessage::APISetWindowTitleRequest: return handle_request(static_cast<const WSAPISetWindowTitleRequest&>(request)); case WSMessage::APIGetWindowTitleRequest: diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index 1d2e3dd159..1f4a2da3bc 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -50,6 +50,7 @@ private: void handle_request(const WSAPISetApplicationMenubarRequest&); void handle_request(const WSAPIAddMenuToMenubarRequest&); void handle_request(const WSAPIAddMenuItemRequest&); + void handle_request(const WSAPIUpdateMenuItemRequest&); void handle_request(const WSAPIAddMenuSeparatorRequest&); void handle_request(const WSAPISetWindowTitleRequest&); void handle_request(const WSAPIGetWindowTitleRequest&); diff --git a/Servers/WindowServer/WSMenu.cpp b/Servers/WindowServer/WSMenu.cpp index 02eeaa7723..38d2e7b445 100644 --- a/Servers/WindowServer/WSMenu.cpp +++ b/Servers/WindowServer/WSMenu.cpp @@ -50,7 +50,8 @@ int WSMenu::height() const void WSMenu::redraw() { - ASSERT(menu_window()); + if (!menu_window()) + return; draw(); menu_window()->invalidate(); } @@ -95,6 +96,8 @@ void WSMenu::draw() painter.fill_rect(item->rect(), WSWindowManager::the().menu_selection_color()); text_color = Color::White; } + if (!item->is_enabled()) + text_color = Color::MidGray; painter.draw_text(item->rect().translated(left_padding(), 0), item->text(), TextAlignment::CenterLeft, text_color); if (!item->shortcut_text().is_empty()) { painter.draw_text(item->rect().translated(-right_padding(), 0), item->shortcut_text(), TextAlignment::CenterRight, text_color); @@ -122,7 +125,8 @@ void WSMenu::on_message(const WSMessage& message) if (message.type() == WSMessage::MouseUp) { if (!m_hovered_item) return; - did_activate(*m_hovered_item); + if (m_hovered_item->is_enabled()) + did_activate(*m_hovered_item); clear_hovered_item(); return; } @@ -152,6 +156,15 @@ void WSMenu::did_activate(WSMenuItem& item) m_client->post_message(message); } +WSMenuItem* WSMenu::item_with_identifier(unsigned identifer) +{ + for (auto& item : m_items) { + if (item->identifier() == identifer) + return item.ptr(); + } + return nullptr; +} + WSMenuItem* WSMenu::item_at(const Point& position) { for (auto& item : m_items) { diff --git a/Servers/WindowServer/WSMenu.h b/Servers/WindowServer/WSMenu.h index 03315358a8..b8b46edc14 100644 --- a/Servers/WindowServer/WSMenu.h +++ b/Servers/WindowServer/WSMenu.h @@ -62,6 +62,7 @@ public: void draw(); const Font& font() const; + WSMenuItem* item_with_identifier(unsigned); WSMenuItem* item_at(const Point&); void redraw(); diff --git a/Servers/WindowServer/WSMenuItem.cpp b/Servers/WindowServer/WSMenuItem.cpp index 49041e0034..95823c86ac 100644 --- a/Servers/WindowServer/WSMenuItem.cpp +++ b/Servers/WindowServer/WSMenuItem.cpp @@ -1,18 +1,30 @@ #include "WSMenuItem.h" +#include "WSMenu.h" -WSMenuItem::WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text) - : m_type(Text) +WSMenuItem::WSMenuItem(WSMenu& menu, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) + : m_menu(menu) + , m_type(Text) + , m_enabled(enabled) , m_identifier(identifier) , m_text(text) , m_shortcut_text(shortcut_text) { } -WSMenuItem::WSMenuItem(Type type) - : m_type(type) +WSMenuItem::WSMenuItem(WSMenu& menu, Type type) + : m_menu(menu) + , m_type(type) { } WSMenuItem::~WSMenuItem() { } + +void WSMenuItem::set_enabled(bool enabled) +{ + if (m_enabled == enabled) + return; + m_enabled = enabled; + m_menu.redraw(); +} diff --git a/Servers/WindowServer/WSMenuItem.h b/Servers/WindowServer/WSMenuItem.h index e3dce736f8..263114648e 100644 --- a/Servers/WindowServer/WSMenuItem.h +++ b/Servers/WindowServer/WSMenuItem.h @@ -4,6 +4,8 @@ #include <AK/Function.h> #include <SharedGraphics/Rect.h> +class WSMenu; + class WSMenuItem { public: enum Type { @@ -12,15 +14,20 @@ public: Separator, }; - explicit WSMenuItem(unsigned identifier, const String& text, const String& shortcut_text = { }); - explicit WSMenuItem(Type); + WSMenuItem(WSMenu&, unsigned identifier, const String& text, const String& shortcut_text = { }, bool enabled = true); + WSMenuItem(WSMenu&, Type); ~WSMenuItem(); Type type() const { return m_type; } - bool enabled() const { return m_enabled; } + + bool is_enabled() const { return m_enabled; } + void set_enabled(bool); String text() const { return m_text; } + void set_text(const String& text) { m_text = text; } + String shortcut_text() const { return m_shortcut_text; } + void set_shortcut_text(const String& text) { m_shortcut_text = text; } void set_rect(const Rect& rect) { m_rect = rect; } Rect rect() const { return m_rect; } @@ -28,6 +35,7 @@ public: unsigned identifier() const { return m_identifier; } private: + WSMenu& m_menu; Type m_type { None }; bool m_enabled { true }; unsigned m_identifier { 0 }; diff --git a/Servers/WindowServer/WSMessage.h b/Servers/WindowServer/WSMessage.h index 4001ac55b8..58b8f31fef 100644 --- a/Servers/WindowServer/WSMessage.h +++ b/Servers/WindowServer/WSMessage.h @@ -38,6 +38,7 @@ public: APIDestroyMenuRequest, APIAddMenuItemRequest, APIAddMenuSeparatorRequest, + APIUpdateMenuItemRequest, APICreateWindowRequest, APIDestroyWindowRequest, APISetWindowTitleRequest, @@ -218,12 +219,13 @@ private: class WSAPIAddMenuItemRequest : public WSAPIClientRequest { public: - WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text) + WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) : WSAPIClientRequest(WSMessage::APIAddMenuItemRequest, client_id) , m_menu_id(menu_id) , m_identifier(identifier) , m_text(text) , m_shortcut_text(shortcut_text) + , m_enabled(enabled) { } @@ -231,12 +233,40 @@ public: unsigned identifier() const { return m_identifier; } String text() const { return m_text; } String shortcut_text() const { return m_shortcut_text; } + bool is_enabled() const { return m_enabled; } private: int m_menu_id { 0 }; unsigned m_identifier { 0 }; String m_text; String m_shortcut_text; + bool m_enabled { true }; +}; + +class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest { +public: + WSAPIUpdateMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled) + : WSAPIClientRequest(WSMessage::APIUpdateMenuItemRequest, client_id) + , m_menu_id(menu_id) + , m_identifier(identifier) + , m_text(text) + , m_shortcut_text(shortcut_text) + , m_enabled(enabled) + { + } + + int menu_id() const { return m_menu_id; } + unsigned identifier() const { return m_identifier; } + String text() const { return m_text; } + String shortcut_text() const { return m_shortcut_text; } + bool is_enabled() const { return m_enabled; } + +private: + int m_menu_id { 0 }; + unsigned m_identifier { 0 }; + String m_text; + String m_shortcut_text; + bool m_enabled { true }; }; class WSAPIAddMenuSeparatorRequest : public WSAPIClientRequest { diff --git a/Servers/WindowServer/WSMessageLoop.cpp b/Servers/WindowServer/WSMessageLoop.cpp index 5c123becfa..d2e31a1204 100644 --- a/Servers/WindowServer/WSMessageLoop.cpp +++ b/Servers/WindowServer/WSMessageLoop.cpp @@ -296,7 +296,12 @@ void WSMessageLoop::on_receive_from_client(int client_id, const WSAPI_ClientMess case WSAPI_ClientMessage::Type::AddMenuItem: ASSERT(message.text_length < (ssize_t)sizeof(message.text)); ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); - post_message(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length))); + post_message(client, make<WSAPIAddMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length), message.menu.enabled)); + break; + case WSAPI_ClientMessage::Type::UpdateMenuItem: + ASSERT(message.text_length < (ssize_t)sizeof(message.text)); + ASSERT(message.menu.shortcut_text_length < (ssize_t)sizeof(message.menu.shortcut_text)); + post_message(client, make<WSAPIUpdateMenuItemRequest>(client_id, message.menu.menu_id, message.menu.identifier, String(message.text, message.text_length), String(message.menu.shortcut_text, message.menu.shortcut_text_length), message.menu.enabled)); break; case WSAPI_ClientMessage::Type::AddMenuSeparator: post_message(client, make<WSAPIAddMenuSeparatorRequest>(client_id, message.menu.menu_id)); diff --git a/Servers/WindowServer/WSWindowManager.cpp b/Servers/WindowServer/WSWindowManager.cpp index 5d8eafdeea..111e5ed181 100644 --- a/Servers/WindowServer/WSWindowManager.cpp +++ b/Servers/WindowServer/WSWindowManager.cpp @@ -95,16 +95,16 @@ WSWindowManager::WSWindowManager() { byte system_menu_name[] = { 0xf8, 0 }; m_system_menu = make<WSMenu>(nullptr, -1, String((const char*)system_menu_name)); - m_system_menu->add_item(make<WSMenuItem>(0, "Open Terminal...")); - m_system_menu->add_item(make<WSMenuItem>(1, "Open ProcessManager...")); - m_system_menu->add_item(make<WSMenuItem>(WSMenuItem::Separator)); - m_system_menu->add_item(make<WSMenuItem>(100, "640x480")); - m_system_menu->add_item(make<WSMenuItem>(101, "800x600")); - m_system_menu->add_item(make<WSMenuItem>(102, "1024x768")); - m_system_menu->add_item(make<WSMenuItem>(103, "1440x900")); - m_system_menu->add_item(make<WSMenuItem>(104, "1920x1080")); - m_system_menu->add_item(make<WSMenuItem>(WSMenuItem::Separator)); - m_system_menu->add_item(make<WSMenuItem>(200, "About...")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 0, "Open Terminal...")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 1, "Open ProcessManager...")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator)); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 100, "640x480")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 101, "800x600")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 102, "1024x768")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 103, "1440x900")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 104, "1920x1080")); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, WSMenuItem::Separator)); + m_system_menu->add_item(make<WSMenuItem>(*m_system_menu, 200, "About...")); m_system_menu->on_item_activation = [this] (WSMenuItem& item) { if (item.identifier() == 0) { if (fork() == 0) { |