mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 18:57:45 +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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GMenu::add_submenu(NonnullOwnPtr<GMenu> submenu)
|
||||||
|
{
|
||||||
|
m_items.append(make<GMenuItem>(m_menu_id, move(submenu)));
|
||||||
|
}
|
||||||
|
|
||||||
void GMenu::add_separator()
|
void GMenu::add_separator()
|
||||||
{
|
{
|
||||||
m_items.append(make<GMenuItem>(m_menu_id, GMenuItem::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)
|
if (m_menu_id == -1)
|
||||||
realize_menu();
|
realize_menu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GMenu::popup(const Point& screen_position)
|
||||||
|
{
|
||||||
|
realize_if_needed();
|
||||||
WSAPI_ClientMessage request;
|
WSAPI_ClientMessage request;
|
||||||
request.type = WSAPI_ClientMessage::Type::PopupMenu;
|
request.type = WSAPI_ClientMessage::Type::PopupMenu;
|
||||||
request.menu.menu_id = m_menu_id;
|
request.menu.menu_id = m_menu_id;
|
||||||
|
@ -87,14 +97,34 @@ int GMenu::realize_menu()
|
||||||
WSAPI_ClientMessage request;
|
WSAPI_ClientMessage request;
|
||||||
request.type = WSAPI_ClientMessage::Type::AddMenuSeparator;
|
request.type = WSAPI_ClientMessage::Type::AddMenuSeparator;
|
||||||
request.menu.menu_id = m_menu_id;
|
request.menu.menu_id = m_menu_id;
|
||||||
|
request.menu.submenu_id = -1;
|
||||||
GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator);
|
GWindowServerConnection::the().sync_request(request, WSAPI_ServerMessage::Type::DidAddMenuSeparator);
|
||||||
continue;
|
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) {
|
if (item.type() == GMenuItem::Action) {
|
||||||
auto& action = *item.action();
|
auto& action = *item.action();
|
||||||
WSAPI_ClientMessage request;
|
WSAPI_ClientMessage request;
|
||||||
request.type = WSAPI_ClientMessage::Type::AddMenuItem;
|
request.type = WSAPI_ClientMessage::Type::AddMenuItem;
|
||||||
request.menu.menu_id = m_menu_id;
|
request.menu.menu_id = m_menu_id;
|
||||||
|
request.menu.submenu_id = -1;
|
||||||
request.menu.identifier = i;
|
request.menu.identifier = i;
|
||||||
request.menu.enabled = action.is_enabled();
|
request.menu.enabled = action.is_enabled();
|
||||||
request.menu.checkable = action.is_checkable();
|
request.menu.checkable = action.is_checkable();
|
||||||
|
|
|
@ -15,10 +15,13 @@ public:
|
||||||
|
|
||||||
static GMenu* from_menu_id(int);
|
static GMenu* from_menu_id(int);
|
||||||
|
|
||||||
|
const String& name() const { return m_name; }
|
||||||
|
|
||||||
GAction* action_at(int);
|
GAction* action_at(int);
|
||||||
|
|
||||||
void add_action(NonnullRefPtr<GAction>);
|
void add_action(NonnullRefPtr<GAction>);
|
||||||
void add_separator();
|
void add_separator();
|
||||||
|
void add_submenu(NonnullOwnPtr<GMenu>);
|
||||||
|
|
||||||
void popup(const Point& screen_position);
|
void popup(const Point& screen_position);
|
||||||
void dismiss();
|
void dismiss();
|
||||||
|
@ -31,6 +34,7 @@ private:
|
||||||
int menu_id() const { return m_menu_id; }
|
int menu_id() const { return m_menu_id; }
|
||||||
int realize_menu();
|
int realize_menu();
|
||||||
void unrealize_menu();
|
void unrealize_menu();
|
||||||
|
void realize_if_needed();
|
||||||
|
|
||||||
int m_menu_id { -1 };
|
int m_menu_id { -1 };
|
||||||
String m_name;
|
String m_name;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <LibGUI/GAction.h>
|
#include <LibGUI/GAction.h>
|
||||||
#include <LibGUI/GEventLoop.h>
|
#include <LibGUI/GEventLoop.h>
|
||||||
|
#include <LibGUI/GMenu.h>
|
||||||
#include <LibGUI/GMenuItem.h>
|
#include <LibGUI/GMenuItem.h>
|
||||||
#include <WindowServer/WSAPITypes.h>
|
#include <WindowServer/WSAPITypes.h>
|
||||||
|
|
||||||
|
@ -21,6 +22,13 @@ GMenuItem::GMenuItem(unsigned menu_id, NonnullRefPtr<GAction>&& action)
|
||||||
m_checked = m_action->is_checked();
|
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()
|
GMenuItem::~GMenuItem()
|
||||||
{
|
{
|
||||||
if (m_action)
|
if (m_action)
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <AK/AKString.h>
|
#include <AK/AKString.h>
|
||||||
#include <AK/Badge.h>
|
#include <AK/Badge.h>
|
||||||
|
#include <AK/NonnullOwnPtr.h>
|
||||||
|
#include <AK/OwnPtr.h>
|
||||||
|
|
||||||
class GAction;
|
class GAction;
|
||||||
class GMenu;
|
class GMenu;
|
||||||
|
@ -11,11 +13,13 @@ public:
|
||||||
enum Type {
|
enum Type {
|
||||||
Invalid,
|
Invalid,
|
||||||
Action,
|
Action,
|
||||||
Separator
|
Separator,
|
||||||
|
Submenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
GMenuItem(unsigned menu_id, Type);
|
GMenuItem(unsigned menu_id, Type);
|
||||||
GMenuItem(unsigned menu_id, NonnullRefPtr<GAction>&&);
|
GMenuItem(unsigned menu_id, NonnullRefPtr<GAction>&&);
|
||||||
|
GMenuItem(unsigned menu_id, NonnullOwnPtr<GMenu>&&);
|
||||||
~GMenuItem();
|
~GMenuItem();
|
||||||
|
|
||||||
Type type() const { return m_type; }
|
Type type() const { return m_type; }
|
||||||
|
@ -24,6 +28,9 @@ public:
|
||||||
GAction* action() { return m_action.ptr(); }
|
GAction* action() { return m_action.ptr(); }
|
||||||
unsigned identifier() const { return m_identifier; }
|
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; }
|
bool is_checkable() const { return m_checkable; }
|
||||||
void set_checkable(bool checkable) { m_checkable = checkable; }
|
void set_checkable(bool checkable) { m_checkable = checkable; }
|
||||||
|
|
||||||
|
@ -46,4 +53,5 @@ private:
|
||||||
bool m_checkable { false };
|
bool m_checkable { false };
|
||||||
bool m_checked { false };
|
bool m_checked { false };
|
||||||
RefPtr<GAction> m_action;
|
RefPtr<GAction> m_action;
|
||||||
|
OwnPtr<GMenu> m_submenu;
|
||||||
};
|
};
|
||||||
|
|
|
@ -263,6 +263,7 @@ struct WSAPI_ClientMessage {
|
||||||
struct {
|
struct {
|
||||||
int menubar_id;
|
int menubar_id;
|
||||||
int menu_id;
|
int menu_id;
|
||||||
|
int submenu_id;
|
||||||
int icon_buffer_id;
|
int icon_buffer_id;
|
||||||
unsigned identifier;
|
unsigned identifier;
|
||||||
char shortcut_text[32];
|
char shortcut_text[32];
|
||||||
|
|
|
@ -159,7 +159,7 @@ bool WSClientConnection::handle_message(const WSAPI_ClientMessage& message, cons
|
||||||
did_misbehave();
|
did_misbehave();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CEventLoop::current().post_event(*this, 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, message.menu.checkable, message.menu.checked, message.menu.icon_buffer_id));
|
CEventLoop::current().post_event(*this, 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, message.menu.checkable, message.menu.checked, message.menu.icon_buffer_id, message.menu.submenu_id));
|
||||||
break;
|
break;
|
||||||
case WSAPI_ClientMessage::Type::UpdateMenuItem:
|
case WSAPI_ClientMessage::Type::UpdateMenuItem:
|
||||||
if (message.text_length > (int)sizeof(message.text)) {
|
if (message.text_length > (int)sizeof(message.text)) {
|
||||||
|
@ -434,6 +434,7 @@ void WSClientConnection::handle_request(const WSAPIAddMenuItemRequest& request)
|
||||||
auto shared_icon = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, icon_buffer.release_nonnull(), { 16, 16 });
|
auto shared_icon = GraphicsBitmap::create_with_shared_buffer(GraphicsBitmap::Format::RGBA32, icon_buffer.release_nonnull(), { 16, 16 });
|
||||||
menu_item->set_icon(shared_icon);
|
menu_item->set_icon(shared_icon);
|
||||||
}
|
}
|
||||||
|
menu_item->set_submenu_id(request.submenu_id());
|
||||||
menu.add_item(move(menu_item));
|
menu.add_item(move(menu_item));
|
||||||
WSAPI_ServerMessage response;
|
WSAPI_ServerMessage response;
|
||||||
response.type = WSAPI_ServerMessage::Type::DidAddMenuItem;
|
response.type = WSAPI_ServerMessage::Type::DidAddMenuItem;
|
||||||
|
|
|
@ -37,6 +37,12 @@ public:
|
||||||
void notify_about_new_screen_rect(const Rect&);
|
void notify_about_new_screen_rect(const Rect&);
|
||||||
void post_paint_message(WSWindow&);
|
void post_paint_message(WSWindow&);
|
||||||
|
|
||||||
|
WSMenu* find_menu_by_id(int menu_id)
|
||||||
|
{
|
||||||
|
// FIXME: Remove this const_cast when Optional knows how to vend a non-const fallback value somehow.
|
||||||
|
return const_cast<WSMenu*>(m_menus.get(menu_id).value_or(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void event(CEvent&) override;
|
virtual void event(CEvent&) override;
|
||||||
|
|
||||||
|
|
|
@ -303,7 +303,7 @@ private:
|
||||||
|
|
||||||
class WSAPIAddMenuItemRequest : public WSAPIClientRequest {
|
class WSAPIAddMenuItemRequest : public WSAPIClientRequest {
|
||||||
public:
|
public:
|
||||||
WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, int icon_buffer_id)
|
WSAPIAddMenuItemRequest(int client_id, int menu_id, unsigned identifier, const String& text, const String& shortcut_text, bool enabled, bool checkable, bool checked, int icon_buffer_id, int submenu_id)
|
||||||
: WSAPIClientRequest(WSEvent::APIAddMenuItemRequest, client_id)
|
: WSAPIClientRequest(WSEvent::APIAddMenuItemRequest, client_id)
|
||||||
, m_menu_id(menu_id)
|
, m_menu_id(menu_id)
|
||||||
, m_identifier(identifier)
|
, m_identifier(identifier)
|
||||||
|
@ -313,6 +313,7 @@ public:
|
||||||
, m_checkable(checkable)
|
, m_checkable(checkable)
|
||||||
, m_checked(checked)
|
, m_checked(checked)
|
||||||
, m_icon_buffer_id(icon_buffer_id)
|
, m_icon_buffer_id(icon_buffer_id)
|
||||||
|
, m_submenu_id(submenu_id)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,6 +325,7 @@ public:
|
||||||
bool is_checkable() const { return m_checkable; }
|
bool is_checkable() const { return m_checkable; }
|
||||||
bool is_checked() const { return m_checked; }
|
bool is_checked() const { return m_checked; }
|
||||||
int icon_buffer_id() const { return m_icon_buffer_id; }
|
int icon_buffer_id() const { return m_icon_buffer_id; }
|
||||||
|
int submenu_id() const { return m_submenu_id; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_menu_id { 0 };
|
int m_menu_id { 0 };
|
||||||
|
@ -334,6 +336,7 @@ private:
|
||||||
bool m_checkable;
|
bool m_checkable;
|
||||||
bool m_checked;
|
bool m_checked;
|
||||||
int m_icon_buffer_id { 0 };
|
int m_icon_buffer_id { 0 };
|
||||||
|
int m_submenu_id { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest {
|
class WSAPIUpdateMenuItemRequest : public WSAPIClientRequest {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "WSEvent.h"
|
#include "WSEvent.h"
|
||||||
#include "WSEventLoop.h"
|
#include "WSEventLoop.h"
|
||||||
#include "WSMenuItem.h"
|
#include "WSMenuItem.h"
|
||||||
|
#include "WSMenuManager.h"
|
||||||
#include "WSScreen.h"
|
#include "WSScreen.h"
|
||||||
#include "WSWindow.h"
|
#include "WSWindow.h"
|
||||||
#include "WSWindowManager.h"
|
#include "WSWindowManager.h"
|
||||||
|
@ -42,9 +43,23 @@ static const char* s_checked_bitmap_data = {
|
||||||
" "
|
" "
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char* s_submenu_arrow_bitmap_data = {
|
||||||
|
" "
|
||||||
|
" # "
|
||||||
|
" ## "
|
||||||
|
" ### "
|
||||||
|
" #### "
|
||||||
|
" ### "
|
||||||
|
" ## "
|
||||||
|
" # "
|
||||||
|
" "
|
||||||
|
};
|
||||||
|
|
||||||
static CharacterBitmap* s_checked_bitmap;
|
static CharacterBitmap* s_checked_bitmap;
|
||||||
static const int s_checked_bitmap_width = 9;
|
static const int s_checked_bitmap_width = 9;
|
||||||
static const int s_checked_bitmap_height = 9;
|
static const int s_checked_bitmap_height = 9;
|
||||||
|
static const int s_submenu_arrow_bitmap_width = 9;
|
||||||
|
static const int s_submenu_arrow_bitmap_height = 9;
|
||||||
static const int s_item_icon_width = 16;
|
static const int s_item_icon_width = 16;
|
||||||
static const int s_checkbox_or_icon_padding = 6;
|
static const int s_checkbox_or_icon_padding = 6;
|
||||||
static const int s_stripe_width = 23;
|
static const int s_stripe_width = 23;
|
||||||
|
@ -163,6 +178,17 @@ void WSMenu::draw()
|
||||||
if (!item.shortcut_text().is_empty()) {
|
if (!item.shortcut_text().is_empty()) {
|
||||||
painter.draw_text(item.rect().translated(-right_padding(), 0), item.shortcut_text(), TextAlignment::CenterRight, text_color);
|
painter.draw_text(item.rect().translated(-right_padding(), 0), item.shortcut_text(), TextAlignment::CenterRight, text_color);
|
||||||
}
|
}
|
||||||
|
if (item.is_submenu()) {
|
||||||
|
static auto& submenu_arrow_bitmap = CharacterBitmap::create_from_ascii(s_submenu_arrow_bitmap_data, s_submenu_arrow_bitmap_width, s_submenu_arrow_bitmap_height).leak_ref();
|
||||||
|
Rect submenu_arrow_rect {
|
||||||
|
item.rect().right() - s_submenu_arrow_bitmap_width - 2,
|
||||||
|
0,
|
||||||
|
s_submenu_arrow_bitmap_width,
|
||||||
|
s_submenu_arrow_bitmap_height
|
||||||
|
};
|
||||||
|
submenu_arrow_rect.center_vertically_within(item.rect());
|
||||||
|
painter.draw_bitmap(submenu_arrow_rect.location(), submenu_arrow_bitmap, Color::Black);
|
||||||
|
}
|
||||||
} else if (item.type() == WSMenuItem::Separator) {
|
} else if (item.type() == WSMenuItem::Separator) {
|
||||||
Point p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1);
|
Point p1(item.rect().translated(stripe_rect.width() + 4, 0).x(), item.rect().center().y() - 1);
|
||||||
Point p2(width - 7, item.rect().center().y() - 1);
|
Point p2(width - 7, item.rect().center().y() - 1);
|
||||||
|
@ -180,6 +206,23 @@ void WSMenu::event(CEvent& event)
|
||||||
if (!item || m_hovered_item == item)
|
if (!item || m_hovered_item == item)
|
||||||
return;
|
return;
|
||||||
m_hovered_item = item;
|
m_hovered_item = item;
|
||||||
|
if (m_hovered_item->is_submenu()) {
|
||||||
|
m_hovered_item->submenu()->popup(m_hovered_item->rect().top_right().translated(menu_window()->rect().location()), true);
|
||||||
|
} else {
|
||||||
|
bool close_remaining_menus = false;
|
||||||
|
for (auto& open_menu : WSWindowManager::the().menu_manager().open_menu_stack()) {
|
||||||
|
if (!open_menu)
|
||||||
|
continue;
|
||||||
|
if (close_remaining_menus) {
|
||||||
|
open_menu->menu_window()->set_visible(false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (open_menu == this) {
|
||||||
|
close_remaining_menus = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
redraw();
|
redraw();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -246,7 +289,7 @@ void WSMenu::close()
|
||||||
menu_window()->set_visible(false);
|
menu_window()->set_visible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSMenu::popup(const Point& position)
|
void WSMenu::popup(const Point& position, bool is_submenu)
|
||||||
{
|
{
|
||||||
ASSERT(!is_empty());
|
ASSERT(!is_empty());
|
||||||
|
|
||||||
|
@ -262,5 +305,5 @@ void WSMenu::popup(const Point& position)
|
||||||
|
|
||||||
window.move_to(adjusted_pos);
|
window.move_to(adjusted_pos);
|
||||||
window.set_visible(true);
|
window.set_visible(true);
|
||||||
WSWindowManager::the().set_current_menu(this);
|
WSWindowManager::the().set_current_menu(this, is_submenu);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void popup(const Point&);
|
void popup(const Point&, bool is_submenu = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void event(CEvent&) override;
|
virtual void event(CEvent&) override;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "WSMenuItem.h"
|
#include "WSMenuItem.h"
|
||||||
|
#include "WSClientConnection.h"
|
||||||
#include "WSMenu.h"
|
#include "WSMenu.h"
|
||||||
#include <LibDraw/GraphicsBitmap.h>
|
#include <LibDraw/GraphicsBitmap.h>
|
||||||
|
|
||||||
|
@ -40,3 +41,10 @@ void WSMenuItem::set_checked(bool checked)
|
||||||
m_checked = checked;
|
m_checked = checked;
|
||||||
m_menu.redraw();
|
m_menu.redraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WSMenu* WSMenuItem::submenu()
|
||||||
|
{
|
||||||
|
ASSERT(is_submenu());
|
||||||
|
ASSERT(m_menu.client());
|
||||||
|
return m_menu.client()->find_menu_by_id(m_submenu_id);
|
||||||
|
}
|
||||||
|
|
|
@ -44,6 +44,12 @@ public:
|
||||||
const GraphicsBitmap* icon() const { return m_icon; }
|
const GraphicsBitmap* icon() const { return m_icon; }
|
||||||
void set_icon(const GraphicsBitmap* icon) { m_icon = icon; }
|
void set_icon(const GraphicsBitmap* icon) { m_icon = icon; }
|
||||||
|
|
||||||
|
bool is_submenu() const { return m_submenu_id != -1; }
|
||||||
|
int submenu_id() const { return m_submenu_id; }
|
||||||
|
void set_submenu_id(int submenu_id) { m_submenu_id = submenu_id; }
|
||||||
|
|
||||||
|
WSMenu* submenu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WSMenu& m_menu;
|
WSMenu& m_menu;
|
||||||
Type m_type { None };
|
Type m_type { None };
|
||||||
|
@ -55,4 +61,5 @@ private:
|
||||||
String m_shortcut_text;
|
String m_shortcut_text;
|
||||||
Rect m_rect;
|
Rect m_rect;
|
||||||
RefPtr<GraphicsBitmap> m_icon;
|
RefPtr<GraphicsBitmap> m_icon;
|
||||||
|
int m_submenu_id { -1 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,6 +31,15 @@ void WSMenuManager::setup()
|
||||||
m_window->set_rect(WSWindowManager::the().menubar_rect());
|
m_window->set_rect(WSWindowManager::the().menubar_rect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WSMenuManager::is_open(const WSMenu& menu) const
|
||||||
|
{
|
||||||
|
for (int i = 0; i < m_open_menu_stack.size(); ++i) {
|
||||||
|
if (&menu == m_open_menu_stack[i].ptr())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void WSMenuManager::draw()
|
void WSMenuManager::draw()
|
||||||
{
|
{
|
||||||
auto& wm = WSWindowManager::the();
|
auto& wm = WSWindowManager::the();
|
||||||
|
@ -43,7 +52,7 @@ void WSMenuManager::draw()
|
||||||
int index = 0;
|
int index = 0;
|
||||||
wm.for_each_active_menubar_menu([&](WSMenu& menu) {
|
wm.for_each_active_menubar_menu([&](WSMenu& menu) {
|
||||||
Color text_color = Color::Black;
|
Color text_color = Color::Black;
|
||||||
if (&menu == wm.current_menu()) {
|
if (is_open(menu)) {
|
||||||
painter.fill_rect(menu.rect_in_menubar(), Color::from_rgb(0xad714f));
|
painter.fill_rect(menu.rect_in_menubar(), Color::from_rgb(0xad714f));
|
||||||
painter.draw_rect(menu.rect_in_menubar(), Color::from_rgb(0x793016));
|
painter.draw_rect(menu.rect_in_menubar(), Color::from_rgb(0x793016));
|
||||||
text_color = Color::White;
|
text_color = Color::White;
|
||||||
|
@ -124,7 +133,9 @@ void WSMenuManager::event(CEvent& event)
|
||||||
void WSMenuManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent& event)
|
void WSMenuManager::handle_menu_mouse_event(WSMenu& menu, const WSMouseEvent& event)
|
||||||
{
|
{
|
||||||
auto& wm = WSWindowManager::the();
|
auto& wm = WSWindowManager::the();
|
||||||
bool is_hover_with_any_menu_open = event.type() == WSMouseEvent::MouseMove && wm.current_menu() && (wm.current_menu()->menubar() || wm.current_menu() == wm.system_menu());
|
bool is_hover_with_any_menu_open = event.type() == WSMouseEvent::MouseMove
|
||||||
|
&& !m_open_menu_stack.is_empty()
|
||||||
|
&& (m_open_menu_stack.first()->menubar() || m_open_menu_stack.first() == wm.system_menu());
|
||||||
bool is_mousedown_with_left_button = event.type() == WSMouseEvent::MouseDown && event.button() == MouseButton::Left;
|
bool is_mousedown_with_left_button = event.type() == WSMouseEvent::MouseDown && event.button() == MouseButton::Left;
|
||||||
bool should_open_menu = &menu != wm.current_menu() && (is_hover_with_any_menu_open || is_mousedown_with_left_button);
|
bool should_open_menu = &menu != wm.current_menu() && (is_hover_with_any_menu_open || is_mousedown_with_left_button);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "WSMenu.h"
|
||||||
#include <LibCore/CObject.h>
|
#include <LibCore/CObject.h>
|
||||||
#include <WindowServer/WSCPUMonitor.h>
|
#include <WindowServer/WSCPUMonitor.h>
|
||||||
#include <WindowServer/WSWindow.h>
|
#include <WindowServer/WSWindow.h>
|
||||||
|
@ -15,6 +16,10 @@ public:
|
||||||
|
|
||||||
virtual void event(CEvent&) override;
|
virtual void event(CEvent&) override;
|
||||||
|
|
||||||
|
bool is_open(const WSMenu&) const;
|
||||||
|
|
||||||
|
Vector<WeakPtr<WSMenu>>& open_menu_stack() { return m_open_menu_stack; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
WSWindow& window() { return *m_window; }
|
WSWindow& window() { return *m_window; }
|
||||||
const WSWindow& window() const { return *m_window; }
|
const WSWindow& window() const { return *m_window; }
|
||||||
|
@ -27,4 +32,6 @@ private:
|
||||||
OwnPtr<WSWindow> m_window;
|
OwnPtr<WSWindow> m_window;
|
||||||
WSCPUMonitor m_cpu_monitor;
|
WSCPUMonitor m_cpu_monitor;
|
||||||
String m_username;
|
String m_username;
|
||||||
|
|
||||||
|
Vector<WeakPtr<WSMenu>> m_open_menu_stack;
|
||||||
};
|
};
|
||||||
|
|
|
@ -211,14 +211,22 @@ int WSWindowManager::menubar_menu_margin() const
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSWindowManager::set_current_menu(WSMenu* menu)
|
void WSWindowManager::set_current_menu(WSMenu* menu, bool is_submenu)
|
||||||
{
|
{
|
||||||
if (m_current_menu == menu)
|
if (m_current_menu == menu)
|
||||||
return;
|
return;
|
||||||
if (m_current_menu)
|
if (!is_submenu && m_current_menu)
|
||||||
m_current_menu->close();
|
m_current_menu->close();
|
||||||
if (menu)
|
if (menu)
|
||||||
m_current_menu = menu->make_weak_ptr();
|
m_current_menu = menu->make_weak_ptr();
|
||||||
|
|
||||||
|
if (!is_submenu) {
|
||||||
|
m_menu_manager.open_menu_stack().clear();
|
||||||
|
if (menu)
|
||||||
|
m_menu_manager.open_menu_stack().append(menu->make_weak_ptr());
|
||||||
|
} else {
|
||||||
|
m_menu_manager.open_menu_stack().append(menu->make_weak_ptr());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
|
void WSWindowManager::set_current_menubar(WSMenuBar* menubar)
|
||||||
|
@ -393,6 +401,11 @@ void WSWindowManager::close_current_menu()
|
||||||
if (m_current_menu && m_current_menu->menu_window())
|
if (m_current_menu && m_current_menu->menu_window())
|
||||||
m_current_menu->menu_window()->set_visible(false);
|
m_current_menu->menu_window()->set_visible(false);
|
||||||
m_current_menu = nullptr;
|
m_current_menu = nullptr;
|
||||||
|
for (auto& menu : m_menu_manager.open_menu_stack()) {
|
||||||
|
if (menu)
|
||||||
|
menu->menu_window()->set_visible(false);
|
||||||
|
}
|
||||||
|
m_menu_manager.open_menu_stack().clear();
|
||||||
m_menu_manager.refresh();
|
m_menu_manager.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,6 +709,18 @@ void WSWindowManager::process_mouse_event(WSMouseEvent& event, WSWindow*& hovere
|
||||||
m_current_menu->clear_hovered_item();
|
m_current_menu->clear_hovered_item();
|
||||||
if (event.type() == WSEvent::MouseDown || event.type() == WSEvent::MouseUp)
|
if (event.type() == WSEvent::MouseDown || event.type() == WSEvent::MouseUp)
|
||||||
close_current_menu();
|
close_current_menu();
|
||||||
|
if (event.type() == WSEvent::MouseMove) {
|
||||||
|
for (auto& menu : m_menu_manager.open_menu_stack()) {
|
||||||
|
if (!menu)
|
||||||
|
continue;
|
||||||
|
if (!menu->menu_window()->rect().contains(event.position()))
|
||||||
|
continue;
|
||||||
|
hovered_window = menu->menu_window();
|
||||||
|
auto translated_event = event.translated(-menu->menu_window()->position());
|
||||||
|
deliver_mouse_event(*menu->menu_window(), translated_event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
hovered_window = &window;
|
hovered_window = &window;
|
||||||
auto translated_event = event.translated(-window.position());
|
auto translated_event = event.translated(-window.position());
|
||||||
|
|
|
@ -78,11 +78,14 @@ public:
|
||||||
|
|
||||||
void draw_window_switcher();
|
void draw_window_switcher();
|
||||||
|
|
||||||
|
WSMenuManager& menu_manager() { return m_menu_manager; }
|
||||||
|
const WSMenuManager& menu_manager() const { return m_menu_manager; }
|
||||||
|
|
||||||
Rect menubar_rect() const;
|
Rect menubar_rect() const;
|
||||||
WSMenuBar* current_menubar() { return m_current_menubar.ptr(); }
|
WSMenuBar* current_menubar() { return m_current_menubar.ptr(); }
|
||||||
void set_current_menubar(WSMenuBar*);
|
void set_current_menubar(WSMenuBar*);
|
||||||
WSMenu* current_menu() { return m_current_menu.ptr(); }
|
WSMenu* current_menu() { return m_current_menu.ptr(); }
|
||||||
void set_current_menu(WSMenu*);
|
void set_current_menu(WSMenu*, bool is_submenu = false);
|
||||||
WSMenu* system_menu() { return m_system_menu.ptr(); }
|
WSMenu* system_menu() { return m_system_menu.ptr(); }
|
||||||
|
|
||||||
const WSCursor& active_cursor() const;
|
const WSCursor& active_cursor() const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue