1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 07:08:10 +00:00

WindowServer+TaskBar: Add a taskbar window button popup menu.

This patch only hooks up the minimize and unminimize actions.
This commit is contained in:
Andreas Kling 2019-04-23 23:14:14 +02:00
parent c5c4e54a67
commit 956bd23aae
17 changed files with 158 additions and 56 deletions

View file

@ -2,6 +2,7 @@ include ../../Makefile.common
OBJS = \ OBJS = \
TaskbarWindow.o \ TaskbarWindow.o \
TaskbarButton.o \
WindowList.o \ WindowList.o \
main.o main.o

View file

@ -0,0 +1,48 @@
#include "TaskbarButton.h"
#include <WindowServer/WSAPITypes.h>
#include <LibGUI/GAction.h>
#include <LibGUI/GMenu.h>
#include <LibGUI/GEventLoop.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)
{
}
TaskbarButton::~TaskbarButton()
{
}
void TaskbarButton::context_menu_event(GContextMenuEvent&)
{
ensure_menu().popup(screen_relative_rect().location(), /* top_anchored */ false);
}
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;
}

View file

@ -0,0 +1,18 @@
#pragma once
#include <LibGUI/GButton.h>
#include "WindowIdentifier.h"
class TaskbarButton final : public GButton {
public:
TaskbarButton(const WindowIdentifier&, GWidget* parent);
virtual ~TaskbarButton() override;
private:
virtual void context_menu_event(GContextMenuEvent&) override;
GMenu& ensure_menu();
WindowIdentifier m_identifier;
OwnPtr<GMenu> m_menu;
};

View file

@ -1,4 +1,5 @@
#include "TaskbarWindow.h" #include "TaskbarWindow.h"
#include "TaskbarButton.h"
#include <LibGUI/GWindow.h> #include <LibGUI/GWindow.h>
#include <LibGUI/GDesktop.h> #include <LibGUI/GDesktop.h>
#include <LibGUI/GEventLoop.h> #include <LibGUI/GEventLoop.h>
@ -30,8 +31,8 @@ TaskbarWindow::TaskbarWindow()
widget->set_frame_shadow(FrameShadow::Raised); widget->set_frame_shadow(FrameShadow::Raised);
set_main_widget(widget); set_main_widget(widget);
m_window_list.aid_create_button = [this] { WindowList::the().aid_create_button = [this] (auto& identifier) {
return create_button(); return create_button(identifier);
}; };
} }
@ -45,9 +46,9 @@ void TaskbarWindow::on_screen_rect_change(const Rect& rect)
set_rect(new_rect); set_rect(new_rect);
} }
GButton* TaskbarWindow::create_button() GButton* TaskbarWindow::create_button(const WindowIdentifier& identifier)
{ {
auto* button = new GButton(main_widget()); auto* button = new TaskbarButton(identifier, main_widget());
button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed); button->set_size_policy(SizePolicy::Fixed, SizePolicy::Fixed);
button->set_preferred_size({ 140, 22 }); button->set_preferred_size({ 140, 22 });
button->set_checkable(true); button->set_checkable(true);
@ -72,7 +73,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
removed_event.window_id() removed_event.window_id()
); );
#endif #endif
m_window_list.remove_window(identifier); WindowList::the().remove_window(identifier);
update(); update();
break; break;
} }
@ -96,7 +97,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
changed_event.icon_path().characters() changed_event.icon_path().characters()
); );
#endif #endif
if (auto* window = m_window_list.window(identifier)) { if (auto* window = WindowList::the().window(identifier)) {
window->set_icon_path(changed_event.icon_path()); window->set_icon_path(changed_event.icon_path());
window->button()->set_icon(window->icon()); window->button()->set_icon(window->icon());
} }
@ -117,7 +118,7 @@ void TaskbarWindow::wm_event(GWMEvent& event)
#endif #endif
if (!should_include_window(changed_event.window_type())) if (!should_include_window(changed_event.window_type()))
break; break;
auto& window = m_window_list.ensure_window(identifier); auto& window = WindowList::the().ensure_window(identifier);
window.set_title(changed_event.title()); window.set_title(changed_event.title());
window.set_rect(changed_event.rect()); window.set_rect(changed_event.rect());
window.set_active(changed_event.is_active()); window.set_active(changed_event.is_active());

View file

@ -13,9 +13,7 @@ public:
private: private:
void on_screen_rect_change(const Rect&); void on_screen_rect_change(const Rect&);
GButton* create_button(); GButton* create_button(const WindowIdentifier&);
virtual void wm_event(GWMEvent&) override; virtual void wm_event(GWMEvent&) override;
WindowList m_window_list;
}; };

View file

@ -2,6 +2,14 @@
#include <WindowServer/WSAPITypes.h> #include <WindowServer/WSAPITypes.h>
#include <LibGUI/GEventLoop.h> #include <LibGUI/GEventLoop.h>
WindowList& WindowList::the()
{
static WindowList* s_the;
if (!s_the)
s_the = new WindowList;
return *s_the;
}
Window* WindowList::window(const WindowIdentifier& identifier) Window* WindowList::window(const WindowIdentifier& identifier)
{ {
auto it = m_windows.find(identifier); auto it = m_windows.find(identifier);
@ -16,7 +24,7 @@ Window& WindowList::ensure_window(const WindowIdentifier& identifier)
if (it != m_windows.end()) if (it != m_windows.end())
return *it->value; return *it->value;
auto window = make<Window>(identifier); auto window = make<Window>(identifier);
window->set_button(aid_create_button()); window->set_button(aid_create_button(identifier));
window->button()->on_click = [identifier] (GButton&) { window->button()->on_click = [identifier] (GButton&) {
WSAPI_ClientMessage message; WSAPI_ClientMessage message;
message.type = WSAPI_ClientMessage::Type::WM_SetActiveWindow; message.type = WSAPI_ClientMessage::Type::WM_SetActiveWindow;

View file

@ -2,37 +2,9 @@
#include <AK/AKString.h> #include <AK/AKString.h>
#include <AK/HashMap.h> #include <AK/HashMap.h>
#include <AK/Traits.h>
#include <SharedGraphics/Rect.h> #include <SharedGraphics/Rect.h>
#include <LibGUI/GButton.h> #include <LibGUI/GButton.h>
#include "WindowIdentifier.h"
class WindowIdentifier {
public:
WindowIdentifier(int client_id, int window_id)
: m_client_id(client_id)
, m_window_id(window_id)
{
}
int client_id() const { return m_client_id; }
int window_id() const { return m_window_id; }
bool operator==(const WindowIdentifier& other) const
{
return m_client_id == other.m_client_id && m_window_id == other.m_window_id;
}
private:
int m_client_id { -1 };
int m_window_id { -1 };
};
namespace AK {
template<>
struct Traits<WindowIdentifier> {
static unsigned hash(const WindowIdentifier& w) { return pair_int_hash(w.client_id(), w.window_id()); }
static void dump(const WindowIdentifier& w) { kprintf("WindowIdentifier(%d, %d)", w.client_id(), w.window_id()); }
};
}
class Window { class Window {
public: public:
@ -90,6 +62,8 @@ private:
class WindowList { class WindowList {
public: public:
static WindowList& the();
template<typename Callback> void for_each_window(Callback callback) template<typename Callback> void for_each_window(Callback callback)
{ {
for (auto& it : m_windows) for (auto& it : m_windows)
@ -100,7 +74,7 @@ public:
Window& ensure_window(const WindowIdentifier&); Window& ensure_window(const WindowIdentifier&);
void remove_window(const WindowIdentifier&); void remove_window(const WindowIdentifier&);
Function<GButton*()> aid_create_button; Function<GButton*(const WindowIdentifier&)> aid_create_button;
private: private:
HashMap<WindowIdentifier, OwnPtr<Window>> m_windows; HashMap<WindowIdentifier, OwnPtr<Window>> m_windows;

View file

@ -39,7 +39,7 @@ 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::popup(const Point& screen_position, bool top_anchored)
{ {
if (!m_menu_id) if (!m_menu_id)
realize_menu(); realize_menu();
@ -47,6 +47,7 @@ void GMenu::popup(const Point& screen_position)
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;
request.menu.position = screen_position; request.menu.position = screen_position;
request.menu.top_anchored = top_anchored;
GEventLoop::post_message_to_server(request); GEventLoop::post_message_to_server(request);
} }

View file

@ -21,7 +21,7 @@ public:
void add_action(Retained<GAction>); void add_action(Retained<GAction>);
void add_separator(); void add_separator();
void popup(const Point& screen_position); void popup(const Point& screen_position, bool top_anchored = true);
void dismiss(); void dismiss();
Function<void(unsigned)> on_item_activation; Function<void(unsigned)> on_item_activation;

View file

@ -210,6 +210,7 @@ struct WSAPI_ClientMessage {
GetWallpaper, GetWallpaper,
SetWindowOverrideCursor, SetWindowOverrideCursor,
WM_SetActiveWindow, WM_SetActiveWindow,
WM_SetWindowMinimized,
PopupMenu, PopupMenu,
DismissMenu, DismissMenu,
SetWindowIcon, SetWindowIcon,
@ -236,6 +237,7 @@ struct WSAPI_ClientMessage {
struct { struct {
int client_id; int client_id;
int window_id; int window_id;
bool minimized;
} wm; } wm;
struct { struct {
int menubar_id; int menubar_id;
@ -245,6 +247,7 @@ struct WSAPI_ClientMessage {
int shortcut_text_length; int shortcut_text_length;
bool enabled; bool enabled;
WSAPI_Point position; WSAPI_Point position;
bool top_anchored;
} menu; } menu;
struct { struct {
WSAPI_Rect rect; WSAPI_Rect rect;

View file

@ -259,7 +259,7 @@ void WSClientConnection::handle_request(const WSAPIPopupMenuRequest& request)
return; return;
} }
auto& menu = *(*it).value; auto& menu = *(*it).value;
menu.popup(position); menu.popup(position, request.top_anchored());
} }
void WSClientConnection::handle_request(const WSAPIDismissMenuRequest& request) void WSClientConnection::handle_request(const WSAPIDismissMenuRequest& request)
@ -641,6 +641,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 WSWMAPISetWindowMinimizedRequest& request)
{
auto* client = WSClientConnection::from_client_id(request.target_client_id());
if (!client) {
post_error("WSWMAPISetWindowMinimizedRequest: Bad client ID");
return;
}
auto it = client->m_windows.find(request.target_window_id());
if (it == client->m_windows.end()) {
post_error("WSWMAPISetWindowMinimizedRequest: Bad window ID");
return;
}
auto& window = *(*it).value;
window.set_minimized(request.is_minimized());
}
void WSClientConnection::on_request(const WSAPIClientRequest& request) void WSClientConnection::on_request(const WSAPIClientRequest& request)
{ {
switch (request.type()) { switch (request.type()) {
@ -700,6 +716,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request)
return handle_request(static_cast<const WSAPISetWindowOverrideCursorRequest&>(request)); return handle_request(static_cast<const WSAPISetWindowOverrideCursorRequest&>(request));
case WSEvent::WMAPISetActiveWindowRequest: case WSEvent::WMAPISetActiveWindowRequest:
return handle_request(static_cast<const WSWMAPISetActiveWindowRequest&>(request)); return handle_request(static_cast<const WSWMAPISetActiveWindowRequest&>(request));
case WSEvent::WMAPISetWindowMinimizedRequest:
return handle_request(static_cast<const WSWMAPISetWindowMinimizedRequest&>(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

@ -73,6 +73,7 @@ private:
void handle_request(const WSAPIGetWallpaperRequest&); void handle_request(const WSAPIGetWallpaperRequest&);
void handle_request(const WSAPISetWindowOverrideCursorRequest&); void handle_request(const WSAPISetWindowOverrideCursorRequest&);
void handle_request(const WSWMAPISetActiveWindowRequest&); void handle_request(const WSWMAPISetActiveWindowRequest&);
void handle_request(const WSWMAPISetWindowMinimizedRequest&);
void handle_request(const WSAPIPopupMenuRequest&); void handle_request(const WSAPIPopupMenuRequest&);
void handle_request(const WSAPIDismissMenuRequest&); void handle_request(const WSAPIDismissMenuRequest&);

View file

@ -61,6 +61,7 @@ public:
APIGetWallpaperRequest, APIGetWallpaperRequest,
APISetWindowOverrideCursorRequest, APISetWindowOverrideCursorRequest,
WMAPISetActiveWindowRequest, WMAPISetActiveWindowRequest,
WMAPISetWindowMinimizedRequest,
APIPopupMenuRequest, APIPopupMenuRequest,
APIDismissMenuRequest, APIDismissMenuRequest,
__End_API_Client_Requests, __End_API_Client_Requests,
@ -120,6 +121,27 @@ private:
int m_target_window_id; int m_target_window_id;
}; };
class WSWMAPISetWindowMinimizedRequest : public WSAPIClientRequest {
public:
WSWMAPISetWindowMinimizedRequest(int client_id, int target_client_id, int target_window_id, bool minimized)
: WSAPIClientRequest(WSEvent::WMAPISetWindowMinimizedRequest, client_id)
, m_target_client_id(target_client_id)
, m_target_window_id(target_window_id)
, m_minimized(minimized)
{
}
int target_client_id() const { return m_target_client_id; }
int target_window_id() const { return m_target_window_id; }
bool is_minimized() const { return m_minimized; }
private:
int m_target_client_id;
int m_target_window_id;
bool m_minimized;
};
class WSAPISetGlobalCursorTrackingRequest : public WSAPIClientRequest { class WSAPISetGlobalCursorTrackingRequest : public WSAPIClientRequest {
public: public:
WSAPISetGlobalCursorTrackingRequest(int client_id, int window_id, bool value) WSAPISetGlobalCursorTrackingRequest(int client_id, int window_id, bool value)
@ -192,19 +214,22 @@ private:
class WSAPIPopupMenuRequest : public WSAPIClientRequest { class WSAPIPopupMenuRequest : public WSAPIClientRequest {
public: public:
WSAPIPopupMenuRequest(int client_id, int menu_id, const Point& position) WSAPIPopupMenuRequest(int client_id, int menu_id, const Point& position, bool top_anchored)
: WSAPIClientRequest(WSEvent::APIPopupMenuRequest, client_id) : WSAPIClientRequest(WSEvent::APIPopupMenuRequest, client_id)
, m_menu_id(menu_id) , m_menu_id(menu_id)
, m_position(position) , m_position(position)
, m_top_anchored(top_anchored)
{ {
} }
int menu_id() const { return m_menu_id; } int menu_id() const { return m_menu_id; }
Point position() const { return m_position; } Point position() const { return m_position; }
bool top_anchored() const { return m_top_anchored; }
private: private:
int m_menu_id; int m_menu_id;
Point m_position; Point m_position;
bool m_top_anchored;
}; };
class WSAPIDismissMenuRequest : public WSAPIClientRequest { class WSAPIDismissMenuRequest : public WSAPIClientRequest {

View file

@ -155,7 +155,7 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
post_event(client, make<WSAPICreateMenuRequest>(client_id, String(message.text, message.text_length))); post_event(client, make<WSAPICreateMenuRequest>(client_id, String(message.text, message.text_length)));
break; break;
case WSAPI_ClientMessage::Type::PopupMenu: case WSAPI_ClientMessage::Type::PopupMenu:
post_event(client, make<WSAPIPopupMenuRequest>(client_id, message.menu.menu_id, message.menu.position)); post_event(client, make<WSAPIPopupMenuRequest>(client_id, message.menu.menu_id, message.menu.position, message.menu.top_anchored));
break; break;
case WSAPI_ClientMessage::Type::DismissMenu: case WSAPI_ClientMessage::Type::DismissMenu:
post_event(client, make<WSAPIDismissMenuRequest>(client_id, message.menu.menu_id)); post_event(client, make<WSAPIDismissMenuRequest>(client_id, message.menu.menu_id));
@ -247,6 +247,9 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag
case WSAPI_ClientMessage::Type::WM_SetActiveWindow: case WSAPI_ClientMessage::Type::WM_SetActiveWindow:
post_event(client, make<WSWMAPISetActiveWindowRequest>(client_id, message.wm.client_id, message.wm.window_id)); post_event(client, make<WSWMAPISetActiveWindowRequest>(client_id, message.wm.client_id, message.wm.window_id));
break; break;
case WSAPI_ClientMessage::Type::WM_SetWindowMinimized:
post_event(client, make<WSWMAPISetWindowMinimizedRequest>(client_id, message.wm.client_id, message.wm.window_id, message.wm.minimized));
break;
default: default:
break; break;
} }

View file

@ -188,11 +188,14 @@ 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 top_anchored)
{ {
ASSERT(!is_empty()); ASSERT(!is_empty());
auto& window = ensure_menu_window(); auto& window = ensure_menu_window();
window.move_to(position); if (top_anchored)
window.move_to(position);
else
window.move_to(position.translated(0, -window.height()));
window.set_visible(true); window.set_visible(true);
WSWindowManager::the().set_current_menu(this); WSWindowManager::the().set_current_menu(this);
} }

View file

@ -74,7 +74,7 @@ public:
void close(); void close();
void popup(const Point&); void popup(const Point&, bool top_anchored);
private: private:
virtual void event(CEvent&) override; virtual void event(CEvent&) override;

View file

@ -258,12 +258,12 @@ IterationDecision WSWindowManager::for_each_visible_window_from_back_to_front(Ca
{ {
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, callback) == IterationDecision::Abort) if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Normal, callback) == IterationDecision::Abort)
return IterationDecision::Abort; return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Abort)
return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, callback) == IterationDecision::Abort) if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Taskbar, callback) == IterationDecision::Abort)
return IterationDecision::Abort; return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == IterationDecision::Abort) if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == IterationDecision::Abort)
return IterationDecision::Abort; return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Menu, callback) == IterationDecision::Abort)
return IterationDecision::Abort;
return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback); return for_each_visible_window_of_type_from_back_to_front(WSWindowType::WindowSwitcher, callback);
} }
@ -293,15 +293,15 @@ IterationDecision WSWindowManager::for_each_visible_window_of_type_from_front_to
template<typename Callback> template<typename Callback>
IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Callback callback) IterationDecision WSWindowManager::for_each_visible_window_from_front_to_back(Callback callback)
{ {
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback) == IterationDecision::Abort)
return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Abort)
return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Taskbar, callback) == IterationDecision::Abort) if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Taskbar, callback) == IterationDecision::Abort)
return IterationDecision::Abort; return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, callback) == IterationDecision::Abort) if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, callback) == IterationDecision::Abort)
return IterationDecision::Abort; return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Menu, callback) == IterationDecision::Abort) return for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback);
return IterationDecision::Abort;
if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback) == IterationDecision::Abort)
return IterationDecision::Abort;
return for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback);
} }
template<typename Callback> template<typename Callback>