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
Notes: sideshowbarker 2024-07-19 13:32:29 +09:00
10 changed files with 92 additions and 35 deletions

View File

@ -1,20 +1,8 @@
#include "TaskbarButton.h"
#include <LibGUI/GAction.h>
#include <LibGUI/GEventLoop.h>
#include <LibGUI/GMenu.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)
: GButton(parent)
, m_identifier(identifier)
@ -27,22 +15,10 @@ TaskbarButton::~TaskbarButton()
void TaskbarButton::context_menu_event(GContextMenuEvent&)
{
ensure_menu().popup(screen_relative_rect().location());
}
GMenu& TaskbarButton::ensure_menu()
{
if (!m_menu) {
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;
WSAPI_ClientMessage request;
request.type = WSAPI_ClientMessage::Type::WM_PopupWindowMenu;
request.wm.client_id = m_identifier.client_id();
request.wm.window_id = m_identifier.window_id();
request.wm.position = screen_relative_rect().location();
GEventLoop::post_message_to_server(request);
}

View File

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

View File

@ -222,6 +222,7 @@ struct WSAPI_ClientMessage {
WM_SetActiveWindow,
WM_SetWindowMinimized,
WM_StartWindowResize,
WM_PopupWindowMenu,
PopupMenu,
DismissMenu,
SetWindowIcon,
@ -251,6 +252,7 @@ struct WSAPI_ClientMessage {
int client_id;
int window_id;
bool minimized;
WSAPI_Point position;
} wm;
struct {
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);
}
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)
{
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));
case WSEvent::WMAPIStartWindowResizeRequest:
return handle_request(static_cast<const WSWMAPIStartWindowResizeRequest&>(request));
case WSEvent::WMAPIPopupWindowMenuRequest:
return handle_request(static_cast<const WSWMAPIPopupWindowMenuRequest&>(request));
case WSEvent::APIPopupMenuRequest:
return handle_request(static_cast<const WSAPIPopupMenuRequest&>(request));
case WSEvent::APIDismissMenuRequest:

View File

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

View File

@ -67,6 +67,7 @@ public:
WMAPISetActiveWindowRequest,
WMAPISetWindowMinimizedRequest,
WMAPIStartWindowResizeRequest,
WMAPIPopupWindowMenuRequest,
APIPopupMenuRequest,
APIDismissMenuRequest,
__End_API_Client_Requests,
@ -129,6 +130,26 @@ private:
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 {
public:
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:
post_event(client, make<WSWMAPIStartWindowResizeRequest>(client_id, message.wm.client_id, message.wm.window_id));
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:
post_event(client, make<WSAPIMoveWindowToFrontRequest>(client_id, message.window_id));
break;

View File

@ -314,3 +314,34 @@ void WSWindow::request_update(const Rect& 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);
virtual ~WSWindow() override;
void popup_window_menu(const Point&);
void request_close();
unsigned wm_event_mask() const { return m_wm_event_mask; }
void set_wm_event_mask(unsigned mask) { m_wm_event_mask = mask; }
@ -177,4 +180,5 @@ private:
unsigned m_wm_event_mask { 0 };
DisjointRectSet m_pending_paint_rects;
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();
m_buttons.append(make<WSButton>(*this, *s_close_button_bitmap, [this](auto&) {
WSEvent close_request(WSEvent::WindowCloseRequest);
m_window.event(close_request);
m_window.request_close();
}));
if (window.is_resizable()) {
@ -271,6 +270,11 @@ void WSWindowFrame::on_mouse_event(const WSMouseEvent& event)
if (m_window.type() != WSWindowType::Normal)
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,
// so that mouse events between the title bar and window contents don't act like
// mouse events on the border.