1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-23 18:25:08 +00:00

WindowServer+Taskbar: Let WindowServer manage the "window menus".

Taskbar now simply asks the WindowServer to popup a window menu when right
clicking on a taskbar button.

This patch also implements the "close" menu item, and furthermore makes the
window menu show up when you left-click a window's titlebar icon. :^)
This commit is contained in:
Andreas Kling 2019-06-21 11:03:43 +02:00
parent da475ce3f5
commit 2e9cc75d11
10 changed files with 92 additions and 35 deletions

View file

@ -1,20 +1,8 @@
#include "TaskbarButton.h" #include "TaskbarButton.h"
#include <LibGUI/GAction.h> #include <LibGUI/GAction.h>
#include <LibGUI/GEventLoop.h> #include <LibGUI/GEventLoop.h>
#include <LibGUI/GMenu.h>
#include <WindowServer/WSAPITypes.h> #include <WindowServer/WSAPITypes.h>
static void set_window_minimized_state(const WindowIdentifier& identifier, bool minimized)
{
WSAPI_ClientMessage message;
message.type = WSAPI_ClientMessage::Type::WM_SetWindowMinimized;
message.wm.client_id = identifier.client_id();
message.wm.window_id = identifier.window_id();
message.wm.minimized = minimized;
bool success = GEventLoop::post_message_to_server(message);
ASSERT(success);
}
TaskbarButton::TaskbarButton(const WindowIdentifier& identifier, GWidget* parent) TaskbarButton::TaskbarButton(const WindowIdentifier& identifier, GWidget* parent)
: GButton(parent) : GButton(parent)
, m_identifier(identifier) , m_identifier(identifier)
@ -27,22 +15,10 @@ TaskbarButton::~TaskbarButton()
void TaskbarButton::context_menu_event(GContextMenuEvent&) void TaskbarButton::context_menu_event(GContextMenuEvent&)
{ {
ensure_menu().popup(screen_relative_rect().location()); WSAPI_ClientMessage request;
} request.type = WSAPI_ClientMessage::Type::WM_PopupWindowMenu;
request.wm.client_id = m_identifier.client_id();
GMenu& TaskbarButton::ensure_menu() request.wm.window_id = m_identifier.window_id();
{ request.wm.position = screen_relative_rect().location();
if (!m_menu) { GEventLoop::post_message_to_server(request);
m_menu = make<GMenu>("");
m_menu->add_action(GAction::create("Minimize", [this](auto&) {
set_window_minimized_state(m_identifier, true);
}));
m_menu->add_action(GAction::create("Unminimize", [this](auto&) {
set_window_minimized_state(m_identifier, false);
}));
m_menu->add_action(GAction::create("Close", [this](auto&) {
dbgprintf("FIXME: Close!\n");
}));
}
return *m_menu;
} }

View file

@ -11,8 +11,5 @@ public:
private: private:
virtual void context_menu_event(GContextMenuEvent&) override; virtual void context_menu_event(GContextMenuEvent&) override;
GMenu& ensure_menu();
WindowIdentifier m_identifier; WindowIdentifier m_identifier;
OwnPtr<GMenu> m_menu;
}; };

View file

@ -222,6 +222,7 @@ struct WSAPI_ClientMessage {
WM_SetActiveWindow, WM_SetActiveWindow,
WM_SetWindowMinimized, WM_SetWindowMinimized,
WM_StartWindowResize, WM_StartWindowResize,
WM_PopupWindowMenu,
PopupMenu, PopupMenu,
DismissMenu, DismissMenu,
SetWindowIcon, SetWindowIcon,
@ -251,6 +252,7 @@ struct WSAPI_ClientMessage {
int client_id; int client_id;
int window_id; int window_id;
bool minimized; bool minimized;
WSAPI_Point position;
} wm; } wm;
struct { struct {
int menubar_id; int menubar_id;

View file

@ -695,6 +695,22 @@ void WSClientConnection::handle_request(const WSWMAPISetActiveWindowRequest& req
WSWindowManager::the().move_to_front_and_make_active(window); WSWindowManager::the().move_to_front_and_make_active(window);
} }
void WSClientConnection::handle_request(const WSWMAPIPopupWindowMenuRequest& request)
{
auto* client = WSClientConnection::from_client_id(request.target_client_id());
if (!client) {
post_error("WSWMAPIPopupWindowMenuRequest: Bad client ID");
return;
}
auto it = client->m_windows.find(request.target_window_id());
if (it == client->m_windows.end()) {
post_error("WSWMAPIPopupWindowMenuRequest: Bad window ID");
return;
}
auto& window = *(*it).value;
window.popup_window_menu(request.position());
}
void WSClientConnection::handle_request(const WSWMAPIStartWindowResizeRequest& request) void WSClientConnection::handle_request(const WSWMAPIStartWindowResizeRequest& request)
{ {
auto* client = WSClientConnection::from_client_id(request.target_client_id()); auto* client = WSClientConnection::from_client_id(request.target_client_id());
@ -792,6 +808,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request)
return handle_request(static_cast<const WSWMAPISetWindowMinimizedRequest&>(request)); return handle_request(static_cast<const WSWMAPISetWindowMinimizedRequest&>(request));
case WSEvent::WMAPIStartWindowResizeRequest: case WSEvent::WMAPIStartWindowResizeRequest:
return handle_request(static_cast<const WSWMAPIStartWindowResizeRequest&>(request)); return handle_request(static_cast<const WSWMAPIStartWindowResizeRequest&>(request));
case WSEvent::WMAPIPopupWindowMenuRequest:
return handle_request(static_cast<const WSWMAPIPopupWindowMenuRequest&>(request));
case WSEvent::APIPopupMenuRequest: case WSEvent::APIPopupMenuRequest:
return handle_request(static_cast<const WSAPIPopupMenuRequest&>(request)); return handle_request(static_cast<const WSAPIPopupMenuRequest&>(request));
case WSEvent::APIDismissMenuRequest: case WSEvent::APIDismissMenuRequest:

View file

@ -77,6 +77,7 @@ private:
void handle_request(const WSWMAPISetActiveWindowRequest&); void handle_request(const WSWMAPISetActiveWindowRequest&);
void handle_request(const WSWMAPISetWindowMinimizedRequest&); void handle_request(const WSWMAPISetWindowMinimizedRequest&);
void handle_request(const WSWMAPIStartWindowResizeRequest&); void handle_request(const WSWMAPIStartWindowResizeRequest&);
void handle_request(const WSWMAPIPopupWindowMenuRequest&);
void handle_request(const WSAPIPopupMenuRequest&); void handle_request(const WSAPIPopupMenuRequest&);
void handle_request(const WSAPIDismissMenuRequest&); void handle_request(const WSAPIDismissMenuRequest&);
void handle_request(const WSAPISetWindowHasAlphaChannelRequest&); void handle_request(const WSAPISetWindowHasAlphaChannelRequest&);

View file

@ -67,6 +67,7 @@ public:
WMAPISetActiveWindowRequest, WMAPISetActiveWindowRequest,
WMAPISetWindowMinimizedRequest, WMAPISetWindowMinimizedRequest,
WMAPIStartWindowResizeRequest, WMAPIStartWindowResizeRequest,
WMAPIPopupWindowMenuRequest,
APIPopupMenuRequest, APIPopupMenuRequest,
APIDismissMenuRequest, APIDismissMenuRequest,
__End_API_Client_Requests, __End_API_Client_Requests,
@ -129,6 +130,26 @@ private:
int m_target_window_id; int m_target_window_id;
}; };
class WSWMAPIPopupWindowMenuRequest : public WSAPIClientRequest {
public:
WSWMAPIPopupWindowMenuRequest(int client_id, int target_client_id, int target_window_id, const Point& position)
: WSAPIClientRequest(WSEvent::WMAPIPopupWindowMenuRequest, client_id)
, m_target_client_id(target_client_id)
, m_target_window_id(target_window_id)
, m_position(position)
{
}
int target_client_id() const { return m_target_client_id; }
int target_window_id() const { return m_target_window_id; }
Point position() const { return m_position; }
private:
int m_target_client_id;
int m_target_window_id;
Point m_position;
};
class WSWMAPISetActiveWindowRequest : public WSAPIClientRequest { class WSWMAPISetActiveWindowRequest : public WSAPIClientRequest {
public: public:
WSWMAPISetActiveWindowRequest(int client_id, int target_client_id, int target_window_id) WSWMAPISetActiveWindowRequest(int client_id, int target_client_id, int target_window_id)

View file

@ -312,6 +312,9 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
case WSAPI_ClientMessage::Type::WM_StartWindowResize: case WSAPI_ClientMessage::Type::WM_StartWindowResize:
post_event(client, make<WSWMAPIStartWindowResizeRequest>(client_id, message.wm.client_id, message.wm.window_id)); post_event(client, make<WSWMAPIStartWindowResizeRequest>(client_id, message.wm.client_id, message.wm.window_id));
break; break;
case WSAPI_ClientMessage::Type::WM_PopupWindowMenu:
post_event(client, make<WSWMAPIPopupWindowMenuRequest>(client_id, message.wm.client_id, message.wm.window_id, message.wm.position));
break;
case WSAPI_ClientMessage::Type::MoveWindowToFront: case WSAPI_ClientMessage::Type::MoveWindowToFront:
post_event(client, make<WSAPIMoveWindowToFrontRequest>(client_id, message.window_id)); post_event(client, make<WSAPIMoveWindowToFrontRequest>(client_id, message.window_id));
break; break;

View file

@ -314,3 +314,34 @@ void WSWindow::request_update(const Rect& rect)
} }
m_pending_paint_rects.add(rect); m_pending_paint_rects.add(rect);
} }
void WSWindow::popup_window_menu(const Point& position)
{
if (!m_window_menu) {
m_window_menu = make<WSMenu>(nullptr, -1, "(Window Menu)");
m_window_menu->add_item(make<WSMenuItem>(*m_window_menu, 1, "Minimize"));
m_window_menu->add_item(make<WSMenuItem>(*m_window_menu, 2, "Unminimize"));
m_window_menu->add_item(make<WSMenuItem>(*m_window_menu, 3, "Close"));
m_window_menu->on_item_activation = [&](auto& item) {
switch (item.identifier()) {
case 1:
set_minimized(true);
break;
case 2:
set_minimized(false);
break;
case 3:
request_close();
break;
}
};
}
m_window_menu->popup(position);
}
void WSWindow::request_close()
{
WSEvent close_request(WSEvent::WindowCloseRequest);
event(close_request);
}

View file

@ -21,6 +21,9 @@ public:
WSWindow(CObject&, WSWindowType); WSWindow(CObject&, WSWindowType);
virtual ~WSWindow() override; virtual ~WSWindow() override;
void popup_window_menu(const Point&);
void request_close();
unsigned wm_event_mask() const { return m_wm_event_mask; } unsigned wm_event_mask() const { return m_wm_event_mask; }
void set_wm_event_mask(unsigned mask) { m_wm_event_mask = mask; } void set_wm_event_mask(unsigned mask) { m_wm_event_mask = mask; }
@ -177,4 +180,5 @@ private:
unsigned m_wm_event_mask { 0 }; unsigned m_wm_event_mask { 0 };
DisjointRectSet m_pending_paint_rects; DisjointRectSet m_pending_paint_rects;
Rect m_unmaximized_rect; Rect m_unmaximized_rect;
OwnPtr<WSMenu> m_window_menu;
}; };

View file

@ -91,8 +91,7 @@ WSWindowFrame::WSWindowFrame(WSWindow& window)
s_unmaximize_button_bitmap = &CharacterBitmap::create_from_ascii(s_unmaximize_button_bitmap_data, s_unmaximize_button_bitmap_width, s_unmaximize_button_bitmap_height).leak_ref(); s_unmaximize_button_bitmap = &CharacterBitmap::create_from_ascii(s_unmaximize_button_bitmap_data, s_unmaximize_button_bitmap_width, s_unmaximize_button_bitmap_height).leak_ref();
m_buttons.append(make<WSButton>(*this, *s_close_button_bitmap, [this](auto&) { m_buttons.append(make<WSButton>(*this, *s_close_button_bitmap, [this](auto&) {
WSEvent close_request(WSEvent::WindowCloseRequest); m_window.request_close();
m_window.event(close_request);
})); }));
if (window.is_resizable()) { if (window.is_resizable()) {
@ -271,6 +270,11 @@ void WSWindowFrame::on_mouse_event(const WSMouseEvent& event)
if (m_window.type() != WSWindowType::Normal) if (m_window.type() != WSWindowType::Normal)
return; return;
if (event.type() == WSEvent::MouseDown && event.button() == MouseButton::Left && title_bar_icon_rect().contains(event.position())) {
m_window.popup_window_menu(event.position().translated(rect().location()));
return;
}
// This is slightly hackish, but expand the title bar rect by one pixel downwards, // This is slightly hackish, but expand the title bar rect by one pixel downwards,
// so that mouse events between the title bar and window contents don't act like // so that mouse events between the title bar and window contents don't act like
// mouse events on the border. // mouse events on the border.