mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 04:37:44 +00:00
WindowServer+LibGUI: Add support for nested menus
It's now possible to add a GMenu as a submenu of another GMenu. Simply use the GMenu::add_submenu(NonnullOwnPtr<GMenu>) API :^) The WindowServer now keeps track of a stack of open menus rather than just one "current menu". This code needs a bit more work, but the basic functionality is now here!
This commit is contained in:
parent
d3ebd8897f
commit
63e6b09816
16 changed files with 177 additions and 12 deletions
|
@ -39,15 +39,25 @@ void GMenu::add_action(NonnullRefPtr<GAction> action)
|
|||
#endif
|
||||
}
|
||||
|
||||
void GMenu::add_submenu(NonnullOwnPtr<GMenu> submenu)
|
||||
{
|
||||
m_items.append(make<GMenuItem>(m_menu_id, move(submenu)));
|
||||
}
|
||||
|
||||
void GMenu::add_separator()
|
||||
{
|
||||
m_items.append(make<GMenuItem>(m_menu_id, GMenuItem::Separator));
|
||||
}
|
||||
|
||||
void GMenu::popup(const Point& screen_position)
|
||||
void GMenu::realize_if_needed()
|
||||
{
|
||||
if (m_menu_id == -1)
|
||||
realize_menu();
|
||||
}
|
||||
|
||||
void GMenu::popup(const Point& screen_position)
|
||||
{
|
||||
realize_if_needed();
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::PopupMenu;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
|
@ -87,14 +97,34 @@ int GMenu::realize_menu()
|
|||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::AddMenuSeparator;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
request.menu.submenu_id = -1;
|
||||
GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator);
|
||||
continue;
|
||||
}
|
||||
if (item.type() == GMenuItem::Submenu) {
|
||||
auto& submenu = *item.submenu();
|
||||
submenu.realize_if_needed();
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::AddMenuItem;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
request.menu.submenu_id = submenu.menu_id();
|
||||
request.menu.identifier = i;
|
||||
// FIXME: It should be possible to disable a submenu.
|
||||
request.menu.enabled = true;
|
||||
request.menu.checkable = false;
|
||||
request.menu.checked = false;
|
||||
ASSERT(submenu.name().length() < (ssize_t)sizeof(request.text));
|
||||
strcpy(request.text, submenu.name().characters());
|
||||
request.text_length = submenu.name().length();
|
||||
GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuItem);
|
||||
continue;
|
||||
}
|
||||
if (item.type() == GMenuItem::Action) {
|
||||
auto& action = *item.action();
|
||||
WSAPI_ClientMessage request;
|
||||
request.type = WSAPI_ClientMessage::Type::AddMenuItem;
|
||||
request.menu.menu_id = m_menu_id;
|
||||
request.menu.submenu_id = -1;
|
||||
request.menu.identifier = i;
|
||||
request.menu.enabled = action.is_enabled();
|
||||
request.menu.checkable = action.is_checkable();
|
||||
|
|
|
@ -15,10 +15,13 @@ public:
|
|||
|
||||
static GMenu* from_menu_id(int);
|
||||
|
||||
const String& name() const { return m_name; }
|
||||
|
||||
GAction* action_at(int);
|
||||
|
||||
void add_action(NonnullRefPtr<GAction>);
|
||||
void add_separator();
|
||||
void add_submenu(NonnullOwnPtr<GMenu>);
|
||||
|
||||
void popup(const Point& screen_position);
|
||||
void dismiss();
|
||||
|
@ -31,6 +34,7 @@ private:
|
|||
int menu_id() const { return m_menu_id; }
|
||||
int realize_menu();
|
||||
void unrealize_menu();
|
||||
void realize_if_needed();
|
||||
|
||||
int m_menu_id { -1 };
|
||||
String m_name;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <LibGUI/GAction.h>
|
||||
#include <LibGUI/GEventLoop.h>
|
||||
#include <LibGUI/GMenu.h>
|
||||
#include <LibGUI/GMenuItem.h>
|
||||
#include <WindowServer/WSAPITypes.h>
|
||||
|
||||
|
@ -21,6 +22,13 @@ GMenuItem::GMenuItem(unsigned menu_id, NonnullRefPtr<GAction>&& action)
|
|||
m_checked = m_action->is_checked();
|
||||
}
|
||||
|
||||
GMenuItem::GMenuItem(unsigned menu_id, NonnullOwnPtr<GMenu>&& submenu)
|
||||
: m_type(Submenu)
|
||||
, m_menu_id(menu_id)
|
||||
, m_submenu(move(submenu))
|
||||
{
|
||||
}
|
||||
|
||||
GMenuItem::~GMenuItem()
|
||||
{
|
||||
if (m_action)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <AK/AKString.h>
|
||||
#include <AK/Badge.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
|
||||
class GAction;
|
||||
class GMenu;
|
||||
|
@ -11,11 +13,13 @@ public:
|
|||
enum Type {
|
||||
Invalid,
|
||||
Action,
|
||||
Separator
|
||||
Separator,
|
||||
Submenu,
|
||||
};
|
||||
|
||||
GMenuItem(unsigned menu_id, Type);
|
||||
GMenuItem(unsigned menu_id, NonnullRefPtr<GAction>&&);
|
||||
GMenuItem(unsigned menu_id, NonnullOwnPtr<GMenu>&&);
|
||||
~GMenuItem();
|
||||
|
||||
Type type() const { return m_type; }
|
||||
|
@ -24,6 +28,9 @@ public:
|
|||
GAction* action() { return m_action.ptr(); }
|
||||
unsigned identifier() const { return m_identifier; }
|
||||
|
||||
GMenu* submenu() { return m_submenu.ptr(); }
|
||||
const GMenu* submenu() const { return m_submenu.ptr(); }
|
||||
|
||||
bool is_checkable() const { return m_checkable; }
|
||||
void set_checkable(bool checkable) { m_checkable = checkable; }
|
||||
|
||||
|
@ -46,4 +53,5 @@ private:
|
|||
bool m_checkable { false };
|
||||
bool m_checked { false };
|
||||
RefPtr<GAction> m_action;
|
||||
OwnPtr<GMenu> m_submenu;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue