From 956bd23aae947039935b01eae00f9736e6614ec9 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 23 Apr 2019 23:14:14 +0200 Subject: [PATCH] WindowServer+TaskBar: Add a taskbar window button popup menu. This patch only hooks up the minimize and unminimize actions. --- Applications/Taskbar/Makefile | 1 + Applications/Taskbar/TaskbarButton.cpp | 48 +++++++++++++++++++++ Applications/Taskbar/TaskbarButton.h | 18 ++++++++ Applications/Taskbar/TaskbarWindow.cpp | 15 ++++--- Applications/Taskbar/TaskbarWindow.h | 4 +- Applications/Taskbar/WindowList.cpp | 10 ++++- Applications/Taskbar/WindowList.h | 34 ++------------- LibGUI/GMenu.cpp | 3 +- LibGUI/GMenu.h | 2 +- Servers/WindowServer/WSAPITypes.h | 3 ++ Servers/WindowServer/WSClientConnection.cpp | 20 ++++++++- Servers/WindowServer/WSClientConnection.h | 1 + Servers/WindowServer/WSEvent.h | 27 +++++++++++- Servers/WindowServer/WSEventLoop.cpp | 5 ++- Servers/WindowServer/WSMenu.cpp | 7 ++- Servers/WindowServer/WSMenu.h | 2 +- Servers/WindowServer/WSWindowManager.h | 14 +++--- 17 files changed, 158 insertions(+), 56 deletions(-) create mode 100644 Applications/Taskbar/TaskbarButton.cpp create mode 100644 Applications/Taskbar/TaskbarButton.h diff --git a/Applications/Taskbar/Makefile b/Applications/Taskbar/Makefile index 4f1ca2d20c..8e15cb6cb6 100644 --- a/Applications/Taskbar/Makefile +++ b/Applications/Taskbar/Makefile @@ -2,6 +2,7 @@ include ../../Makefile.common OBJS = \ TaskbarWindow.o \ + TaskbarButton.o \ WindowList.o \ main.o diff --git a/Applications/Taskbar/TaskbarButton.cpp b/Applications/Taskbar/TaskbarButton.cpp new file mode 100644 index 0000000000..0deea6309c --- /dev/null +++ b/Applications/Taskbar/TaskbarButton.cpp @@ -0,0 +1,48 @@ +#include "TaskbarButton.h" +#include +#include +#include +#include + +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(""); + 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; +} diff --git a/Applications/Taskbar/TaskbarButton.h b/Applications/Taskbar/TaskbarButton.h new file mode 100644 index 0000000000..6cb790521a --- /dev/null +++ b/Applications/Taskbar/TaskbarButton.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#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 m_menu; +}; diff --git a/Applications/Taskbar/TaskbarWindow.cpp b/Applications/Taskbar/TaskbarWindow.cpp index 562d6aad63..126a60bd73 100644 --- a/Applications/Taskbar/TaskbarWindow.cpp +++ b/Applications/Taskbar/TaskbarWindow.cpp @@ -1,4 +1,5 @@ #include "TaskbarWindow.h" +#include "TaskbarButton.h" #include #include #include @@ -30,8 +31,8 @@ TaskbarWindow::TaskbarWindow() widget->set_frame_shadow(FrameShadow::Raised); set_main_widget(widget); - m_window_list.aid_create_button = [this] { - return create_button(); + WindowList::the().aid_create_button = [this] (auto& identifier) { + return create_button(identifier); }; } @@ -45,9 +46,9 @@ void TaskbarWindow::on_screen_rect_change(const Rect& 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_preferred_size({ 140, 22 }); button->set_checkable(true); @@ -72,7 +73,7 @@ void TaskbarWindow::wm_event(GWMEvent& event) removed_event.window_id() ); #endif - m_window_list.remove_window(identifier); + WindowList::the().remove_window(identifier); update(); break; } @@ -96,7 +97,7 @@ void TaskbarWindow::wm_event(GWMEvent& event) changed_event.icon_path().characters() ); #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->button()->set_icon(window->icon()); } @@ -117,7 +118,7 @@ void TaskbarWindow::wm_event(GWMEvent& event) #endif if (!should_include_window(changed_event.window_type())) break; - auto& window = m_window_list.ensure_window(identifier); + auto& window = WindowList::the().ensure_window(identifier); window.set_title(changed_event.title()); window.set_rect(changed_event.rect()); window.set_active(changed_event.is_active()); diff --git a/Applications/Taskbar/TaskbarWindow.h b/Applications/Taskbar/TaskbarWindow.h index 681d7b9181..856185343f 100644 --- a/Applications/Taskbar/TaskbarWindow.h +++ b/Applications/Taskbar/TaskbarWindow.h @@ -13,9 +13,7 @@ public: private: void on_screen_rect_change(const Rect&); - GButton* create_button(); + GButton* create_button(const WindowIdentifier&); virtual void wm_event(GWMEvent&) override; - - WindowList m_window_list; }; diff --git a/Applications/Taskbar/WindowList.cpp b/Applications/Taskbar/WindowList.cpp index c149853ea3..35f0c1a3cb 100644 --- a/Applications/Taskbar/WindowList.cpp +++ b/Applications/Taskbar/WindowList.cpp @@ -2,6 +2,14 @@ #include #include +WindowList& WindowList::the() +{ + static WindowList* s_the; + if (!s_the) + s_the = new WindowList; + return *s_the; +} + Window* WindowList::window(const WindowIdentifier& identifier) { auto it = m_windows.find(identifier); @@ -16,7 +24,7 @@ Window& WindowList::ensure_window(const WindowIdentifier& identifier) if (it != m_windows.end()) return *it->value; auto window = make(identifier); - window->set_button(aid_create_button()); + window->set_button(aid_create_button(identifier)); window->button()->on_click = [identifier] (GButton&) { WSAPI_ClientMessage message; message.type = WSAPI_ClientMessage::Type::WM_SetActiveWindow; diff --git a/Applications/Taskbar/WindowList.h b/Applications/Taskbar/WindowList.h index 458222c5b8..6ebaaa5328 100644 --- a/Applications/Taskbar/WindowList.h +++ b/Applications/Taskbar/WindowList.h @@ -2,37 +2,9 @@ #include #include -#include #include #include - -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 { - 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()); } -}; -} +#include "WindowIdentifier.h" class Window { public: @@ -90,6 +62,8 @@ private: class WindowList { public: + static WindowList& the(); + template void for_each_window(Callback callback) { for (auto& it : m_windows) @@ -100,7 +74,7 @@ public: Window& ensure_window(const WindowIdentifier&); void remove_window(const WindowIdentifier&); - Function aid_create_button; + Function aid_create_button; private: HashMap> m_windows; diff --git a/LibGUI/GMenu.cpp b/LibGUI/GMenu.cpp index 13be51b2d4..1bc0fc4f9a 100644 --- a/LibGUI/GMenu.cpp +++ b/LibGUI/GMenu.cpp @@ -39,7 +39,7 @@ void GMenu::add_separator() m_items.append(make(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) realize_menu(); @@ -47,6 +47,7 @@ void GMenu::popup(const Point& screen_position) request.type = WSAPI_ClientMessage::Type::PopupMenu; request.menu.menu_id = m_menu_id; request.menu.position = screen_position; + request.menu.top_anchored = top_anchored; GEventLoop::post_message_to_server(request); } diff --git a/LibGUI/GMenu.h b/LibGUI/GMenu.h index a5280393dc..3019444557 100644 --- a/LibGUI/GMenu.h +++ b/LibGUI/GMenu.h @@ -21,7 +21,7 @@ public: void add_action(Retained); void add_separator(); - void popup(const Point& screen_position); + void popup(const Point& screen_position, bool top_anchored = true); void dismiss(); Function on_item_activation; diff --git a/Servers/WindowServer/WSAPITypes.h b/Servers/WindowServer/WSAPITypes.h index e874ab4474..5c1cd50a2f 100644 --- a/Servers/WindowServer/WSAPITypes.h +++ b/Servers/WindowServer/WSAPITypes.h @@ -210,6 +210,7 @@ struct WSAPI_ClientMessage { GetWallpaper, SetWindowOverrideCursor, WM_SetActiveWindow, + WM_SetWindowMinimized, PopupMenu, DismissMenu, SetWindowIcon, @@ -236,6 +237,7 @@ struct WSAPI_ClientMessage { struct { int client_id; int window_id; + bool minimized; } wm; struct { int menubar_id; @@ -245,6 +247,7 @@ struct WSAPI_ClientMessage { int shortcut_text_length; bool enabled; WSAPI_Point position; + bool top_anchored; } menu; struct { WSAPI_Rect rect; diff --git a/Servers/WindowServer/WSClientConnection.cpp b/Servers/WindowServer/WSClientConnection.cpp index a0efa9e258..b52bcf00e4 100644 --- a/Servers/WindowServer/WSClientConnection.cpp +++ b/Servers/WindowServer/WSClientConnection.cpp @@ -259,7 +259,7 @@ void WSClientConnection::handle_request(const WSAPIPopupMenuRequest& request) return; } auto& menu = *(*it).value; - menu.popup(position); + menu.popup(position, request.top_anchored()); } 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); } +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) { switch (request.type()) { @@ -700,6 +716,8 @@ void WSClientConnection::on_request(const WSAPIClientRequest& request) return handle_request(static_cast(request)); case WSEvent::WMAPISetActiveWindowRequest: return handle_request(static_cast(request)); + case WSEvent::WMAPISetWindowMinimizedRequest: + return handle_request(static_cast(request)); case WSEvent::APIPopupMenuRequest: return handle_request(static_cast(request)); case WSEvent::APIDismissMenuRequest: diff --git a/Servers/WindowServer/WSClientConnection.h b/Servers/WindowServer/WSClientConnection.h index eea8ac43c4..8db0db1e30 100644 --- a/Servers/WindowServer/WSClientConnection.h +++ b/Servers/WindowServer/WSClientConnection.h @@ -73,6 +73,7 @@ private: void handle_request(const WSAPIGetWallpaperRequest&); void handle_request(const WSAPISetWindowOverrideCursorRequest&); void handle_request(const WSWMAPISetActiveWindowRequest&); + void handle_request(const WSWMAPISetWindowMinimizedRequest&); void handle_request(const WSAPIPopupMenuRequest&); void handle_request(const WSAPIDismissMenuRequest&); diff --git a/Servers/WindowServer/WSEvent.h b/Servers/WindowServer/WSEvent.h index c2d96f8821..e1a9d80fb0 100644 --- a/Servers/WindowServer/WSEvent.h +++ b/Servers/WindowServer/WSEvent.h @@ -61,6 +61,7 @@ public: APIGetWallpaperRequest, APISetWindowOverrideCursorRequest, WMAPISetActiveWindowRequest, + WMAPISetWindowMinimizedRequest, APIPopupMenuRequest, APIDismissMenuRequest, __End_API_Client_Requests, @@ -120,6 +121,27 @@ private: 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 { public: WSAPISetGlobalCursorTrackingRequest(int client_id, int window_id, bool value) @@ -192,19 +214,22 @@ private: class WSAPIPopupMenuRequest : public WSAPIClientRequest { 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) , m_menu_id(menu_id) , m_position(position) + , m_top_anchored(top_anchored) { } int menu_id() const { return m_menu_id; } Point position() const { return m_position; } + bool top_anchored() const { return m_top_anchored; } private: int m_menu_id; Point m_position; + bool m_top_anchored; }; class WSAPIDismissMenuRequest : public WSAPIClientRequest { diff --git a/Servers/WindowServer/WSEventLoop.cpp b/Servers/WindowServer/WSEventLoop.cpp index 3e80ef08df..9c3d19654d 100644 --- a/Servers/WindowServer/WSEventLoop.cpp +++ b/Servers/WindowServer/WSEventLoop.cpp @@ -155,7 +155,7 @@ bool WSEventLoop::on_receive_from_client(int client_id, const WSAPI_ClientMessag post_event(client, make(client_id, String(message.text, message.text_length))); break; case WSAPI_ClientMessage::Type::PopupMenu: - post_event(client, make(client_id, message.menu.menu_id, message.menu.position)); + post_event(client, make(client_id, message.menu.menu_id, message.menu.position, message.menu.top_anchored)); break; case WSAPI_ClientMessage::Type::DismissMenu: post_event(client, make(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: post_event(client, make(client_id, message.wm.client_id, message.wm.window_id)); break; + case WSAPI_ClientMessage::Type::WM_SetWindowMinimized: + post_event(client, make(client_id, message.wm.client_id, message.wm.window_id, message.wm.minimized)); + break; default: break; } diff --git a/Servers/WindowServer/WSMenu.cpp b/Servers/WindowServer/WSMenu.cpp index 9c02de1b17..877f074c48 100644 --- a/Servers/WindowServer/WSMenu.cpp +++ b/Servers/WindowServer/WSMenu.cpp @@ -188,11 +188,14 @@ void WSMenu::close() menu_window()->set_visible(false); } -void WSMenu::popup(const Point& position) +void WSMenu::popup(const Point& position, bool top_anchored) { ASSERT(!is_empty()); 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); WSWindowManager::the().set_current_menu(this); } diff --git a/Servers/WindowServer/WSMenu.h b/Servers/WindowServer/WSMenu.h index 70d9cedb55..b0fefff92f 100644 --- a/Servers/WindowServer/WSMenu.h +++ b/Servers/WindowServer/WSMenu.h @@ -74,7 +74,7 @@ public: void close(); - void popup(const Point&); + void popup(const Point&, bool top_anchored); private: virtual void event(CEvent&) override; diff --git a/Servers/WindowServer/WSWindowManager.h b/Servers/WindowServer/WSWindowManager.h index 45d4d53717..304d902c6b 100644 --- a/Servers/WindowServer/WSWindowManager.h +++ b/Servers/WindowServer/WSWindowManager.h @@ -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) 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) return IterationDecision::Abort; if (for_each_visible_window_of_type_from_back_to_front(WSWindowType::Tooltip, callback) == 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); } @@ -293,15 +293,15 @@ IterationDecision WSWindowManager::for_each_visible_window_of_type_from_front_to template 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) return IterationDecision::Abort; if (for_each_visible_window_of_type_from_front_to_back(WSWindowType::Tooltip, 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::Normal, callback) == IterationDecision::Abort) - return IterationDecision::Abort; - return for_each_visible_window_of_type_from_front_to_back(WSWindowType::WindowSwitcher, callback); + return for_each_visible_window_of_type_from_front_to_back(WSWindowType::Normal, callback); } template