WindowServer+Taskbar: Show applets in taskbar :^)

WindowServer now collects applet windows into an "applet area" which is
really just a window that a WM (window management) client can position
via IPC.

This is rather hackish, and I think we should come up with a better
architecture eventually, but this brings back the missing applets since
the global menu where they used to live is gone.
This commit is contained in:
Andreas Kling 2021-03-30 22:41:14 +02:00
parent 44602ae141
commit 9bbc1c9c93
Notes: sideshowbarker 2024-07-18 20:56:46 +09:00
18 changed files with 145 additions and 40 deletions

View File

@ -77,6 +77,7 @@ public:
WM_WindowStateChanged,
WM_WindowRectChanged,
WM_WindowIconBitmapChanged,
WM_AppletAreaSizeChanged,
__End_WM_Events,
};
@ -108,6 +109,20 @@ private:
int m_window_id { -1 };
};
class WMAppletAreaSizeChangedEvent : public WMEvent {
public:
explicit WMAppletAreaSizeChangedEvent(const Gfx::IntSize& size)
: WMEvent(Event::Type::WM_AppletAreaSizeChanged, 0, 0)
, m_size(size)
{
}
const Gfx::IntSize& size() const { return m_size; }
private:
Gfx::IntSize m_size;
};
class WMWindowRemovedEvent : public WMEvent {
public:
WMWindowRemovedEvent(int client_id, int window_id)

View File

@ -269,6 +269,12 @@ void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowState
Core::EventLoop::current().post_event(*window, make<WMWindowStateChangedEvent>(message.client_id(), message.window_id(), message.parent_client_id(), message.parent_window_id(), message.title(), message.rect(), message.is_active(), message.is_modal(), static_cast<WindowType>(message.window_type()), message.is_minimized(), message.is_frameless(), message.progress()));
}
void WindowServerConnection::handle(const Messages::WindowClient::WM_AppletAreaSizeChanged& message)
{
if (auto* window = Window::from_window_id(message.wm_id()))
Core::EventLoop::current().post_event(*window, make<WMAppletAreaSizeChangedEvent>(message.size()));
}
void WindowServerConnection::handle(const Messages::WindowClient::WM_WindowRectChanged& message)
{
if (auto* window = Window::from_window_id(message.wm_id()))

View File

@ -69,6 +69,7 @@ private:
virtual void handle(const Messages::WindowClient::WM_WindowStateChanged&) override;
virtual void handle(const Messages::WindowClient::WM_WindowIconBitmapChanged&) override;
virtual void handle(const Messages::WindowClient::WM_WindowRectChanged&) override;
virtual void handle(const Messages::WindowClient::WM_AppletAreaSizeChanged&) override;
virtual void handle(const Messages::WindowClient::AsyncSetWallpaperFinished&) override;
virtual void handle(const Messages::WindowClient::DragDropped&) override;
virtual void handle(const Messages::WindowClient::DragAccepted&) override;

View File

@ -88,7 +88,7 @@ TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
auto& start_button = main_widget.add<GUI::Button>("Serenity");
start_button.set_font(Gfx::FontDatabase::default_bold_font());
start_button.set_icon_spacing(0);
start_button.set_fixed_width(80);
start_button.set_fixed_size(80, 22);
auto app_icon = GUI::Icon::default_icon("ladybug");
start_button.set_icon(app_icon.bitmap_for_size(16));
@ -104,6 +104,11 @@ TaskbarWindow::TaskbarWindow(NonnullRefPtr<GUI::Menu> start_menu)
m_default_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png");
m_applet_area_container = main_widget.add<GUI::Frame>();
m_applet_area_container->set_frame_thickness(1);
m_applet_area_container->set_frame_shape(Gfx::FrameShape::Box);
m_applet_area_container->set_frame_shadow(Gfx::FrameShadow::Sunken);
main_widget.add<Taskbar::ClockWidget>();
}
@ -175,8 +180,8 @@ void TaskbarWindow::on_screen_rect_change(const Gfx::IntRect& rect)
NonnullRefPtr<GUI::Button> TaskbarWindow::create_button(const WindowIdentifier& identifier)
{
auto& button = m_task_button_container->add<TaskbarButton>(identifier);
button.set_min_size(20, 23);
button.set_max_size(140, 23);
button.set_min_size(20, 22);
button.set_max_size(140, 22);
button.set_text_alignment(Gfx::TextAlignment::CenterLeft);
button.set_icon(*m_default_icon);
return button;
@ -317,6 +322,17 @@ void TaskbarWindow::wm_event(GUI::WMEvent& event)
}
break;
}
case GUI::Event::WM_AppletAreaSizeChanged: {
auto& changed_event = static_cast<GUI::WMAppletAreaSizeChangedEvent&>(event);
m_applet_area_container->set_fixed_size(changed_event.size().width() + 8, 22);
// NOTE: Widget layout is normally lazy, but here we have to force it right away so we can tell
// WindowServer where to place the applet area window.
main_widget()->do_layout();
Gfx::IntRect new_rect { {}, changed_event.size() };
new_rect.center_within(m_applet_area_container->screen_relative_rect());
GUI::WindowServerConnection::the().send_sync<Messages::WindowServer::WM_SetAppletAreaPosition>(new_rect.location());
break;
}
default:
break;
}

View File

@ -53,4 +53,5 @@ private:
NonnullRefPtr<GUI::Menu> m_start_menu;
RefPtr<GUI::Widget> m_task_button_container;
RefPtr<Gfx::Bitmap> m_default_icon;
RefPtr<GUI::Frame> m_applet_area_container;
};

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2020, Hüseyin Aslıtürk <asliturk@hotmail.com>
* Copyright (c) 2021, Andreas Kling <kling@serenityos.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -53,16 +54,26 @@ AppletManager& AppletManager::the()
return *s_the;
}
void AppletManager::set_position(const Gfx::IntPoint& position)
{
m_window->move_to(position);
m_window->set_visible(true);
}
void AppletManager::event(Core::Event& event)
{
if (!is<MouseEvent>(event))
return;
auto& mouse_event = static_cast<MouseEvent&>(event);
dbgln("mouse_event: {}", mouse_event.position());
for (auto& applet : m_applets) {
if (!applet)
continue;
if (!applet->rect_in_menubar().contains(mouse_event.position()))
if (!applet->rect_in_applet_area().contains(mouse_event.position()))
continue;
auto local_event = mouse_event.translated(-applet->rect_in_menubar().location());
auto local_event = mouse_event.translated(-applet->rect_in_applet_area().location());
applet->dispatch_event(local_event);
}
}
@ -82,24 +93,50 @@ void AppletManager::add_applet(Window& applet)
return index_a.value_or(0) > index_b.value_or(0);
});
calculate_applet_rects(MenuManager::the().window());
relayout();
MenuManager::the().refresh();
}
void AppletManager::calculate_applet_rects(Window& window)
void AppletManager::relayout()
{
auto menubar_rect = window.rect();
int right_edge_x = menubar_rect.width() - 4;
constexpr int applet_spacing = 4;
constexpr int applet_window_height = 20;
int total_width = 0;
for (auto& existing_applet : m_applets) {
Gfx::IntRect new_applet_rect(right_edge_x - existing_applet->size().width(), 0, existing_applet->size().width(), existing_applet->size().height());
Gfx::IntRect dummy_menubar_rect(0, 0, 0, 18);
new_applet_rect.center_vertically_within(dummy_menubar_rect);
existing_applet->set_rect_in_menubar(new_applet_rect);
right_edge_x = existing_applet->rect_in_menubar().x() - 4;
total_width += max(0, existing_applet->size().width()) + applet_spacing;
}
if (total_width > 0)
total_width -= applet_spacing;
auto right_edge_x = total_width;
for (auto& existing_applet : m_applets) {
auto applet_size = existing_applet->size();
Gfx::IntRect new_applet_rect(right_edge_x - applet_size.width(), 0, applet_size.width(), applet_size.height());
Gfx::IntRect dummy_container_rect(0, 0, 0, applet_window_height);
new_applet_rect.center_vertically_within(dummy_container_rect);
existing_applet->set_rect_in_applet_area(new_applet_rect);
right_edge_x = existing_applet->rect_in_applet_area().x() - applet_spacing;
}
if (!m_window) {
m_window = Window::construct(*this, WindowType::AppletArea);
m_window->set_visible(false);
}
Gfx::IntRect rect { m_window->position(), Gfx::IntSize { total_width, applet_window_height } };
if (m_window->rect() == rect)
return;
m_window->set_rect(rect);
if (!rect.is_empty()) {
Gfx::Painter painter(*m_window->backing_store());
painter.fill_rect(m_window->rect(), WindowManager::the().palette().window());
}
WindowManager::the().tell_wm_listeners_applet_area_size_changed(rect.size());
}
void AppletManager::remove_applet(Window& applet)
@ -108,7 +145,7 @@ void AppletManager::remove_applet(Window& applet)
return &applet == entry.ptr();
});
MenuManager::the().refresh();
relayout();
}
void AppletManager::draw()
@ -125,17 +162,20 @@ void AppletManager::draw_applet(const Window& applet)
if (!applet.backing_store())
return;
Gfx::Painter painter(*MenuManager::the().window().backing_store());
Gfx::Painter painter(*m_window->backing_store());
Gfx::PainterStateSaver saver(painter);
painter.add_clip_rect(applet.rect_in_menubar());
painter.fill_rect(applet.rect_in_menubar(), WindowManager::the().palette().window());
painter.blit(applet.rect_in_menubar().location(), *applet.backing_store(), applet.backing_store()->rect());
painter.add_clip_rect(applet.rect_in_applet_area());
painter.fill_rect(applet.rect_in_applet_area(), WindowManager::the().palette().window());
painter.blit(applet.rect_in_applet_area().location(), *applet.backing_store(), applet.backing_store()->rect());
}
void AppletManager::invalidate_applet(const Window& applet, const Gfx::IntRect& rect)
void AppletManager::invalidate_applet(const Window& applet, const Gfx::IntRect&)
{
draw_applet(applet);
MenuManager::the().window().invalidate(rect.translated(applet.rect_in_menubar().location()));
draw();
// FIXME: Invalidate only the exact rect we've been given.
if (m_window)
m_window->invalidate();
}
}

View File

@ -45,12 +45,18 @@ public:
void remove_applet(Window& applet);
void draw();
void invalidate_applet(const Window& applet, const Gfx::IntRect& rect);
void calculate_applet_rects(Window& window);
void relayout();
void set_position(const Gfx::IntPoint&);
Window* window() { return m_window; }
const Window* window() const { return m_window; }
private:
void draw_applet(const Window& applet);
Vector<WeakPtr<Window>> m_applets;
RefPtr<Window> m_window;
};
}

View File

@ -483,7 +483,7 @@ OwnPtr<Messages::WindowServer::GetWindowRectInMenubarResponse> ClientConnection:
did_misbehave("GetWindowRectInMenubar: Bad window ID");
return {};
}
return make<Messages::WindowServer::GetWindowRectInMenubarResponse>(it->value->rect_in_menubar());
return make<Messages::WindowServer::GetWindowRectInMenubarResponse>(it->value->rect_in_applet_area());
}
Window* ClientConnection::window_from_id(i32 window_id)
@ -717,6 +717,12 @@ OwnPtr<Messages::WindowServer::SetWindowAlphaHitThresholdResponse> ClientConnect
return make<Messages::WindowServer::SetWindowAlphaHitThresholdResponse>();
}
OwnPtr<Messages::WindowServer::WM_SetAppletAreaPositionResponse> ClientConnection::handle(const Messages::WindowServer::WM_SetAppletAreaPosition& message)
{
AppletManager::the().set_position(message.position());
return make<Messages::WindowServer::WM_SetAppletAreaPositionResponse>();
}
void ClientConnection::handle(const Messages::WindowServer::WM_SetActiveWindow& message)
{
auto* client = ClientConnection::from_client_id(message.client_id());

View File

@ -136,6 +136,7 @@ private:
virtual void handle(const Messages::WindowServer::WM_SetWindowMinimized&) override;
virtual void handle(const Messages::WindowServer::WM_StartWindowResize&) override;
virtual void handle(const Messages::WindowServer::WM_PopupWindowMenu&) override;
virtual OwnPtr<Messages::WindowServer::WM_SetAppletAreaPositionResponse> handle(const Messages::WindowServer::WM_SetAppletAreaPosition&) override;
virtual OwnPtr<Messages::WindowServer::SetWindowHasAlphaChannelResponse> handle(const Messages::WindowServer::SetWindowHasAlphaChannel&) override;
virtual OwnPtr<Messages::WindowServer::SetWindowAlphaHitThresholdResponse> handle(const Messages::WindowServer::SetWindowAlphaHitThreshold&) override;
virtual OwnPtr<Messages::WindowServer::MoveWindowToFrontResponse> handle(const Messages::WindowServer::MoveWindowToFront&) override;

View File

@ -28,10 +28,7 @@
#include <AK/Badge.h>
#include <AK/Debug.h>
#include <AK/QuickSort.h>
#include <LibCore/DirIterator.h>
#include <LibGfx/Font.h>
#include <LibGfx/Painter.h>
#include <WindowServer/AppletManager.h>
#include <WindowServer/ClientConnection.h>
#include <WindowServer/MenuManager.h>
#include <WindowServer/Screen.h>
@ -84,7 +81,6 @@ void MenuManager::draw()
if (m_needs_window_resize) {
m_window->set_rect(menubar_rect);
AppletManager::the().calculate_applet_rects(window());
m_needs_window_resize = false;
}
@ -93,8 +89,6 @@ void MenuManager::draw()
painter.fill_rect(menubar_rect, palette.window());
painter.draw_line({ 0, menubar_rect.bottom() - 1 }, { menubar_rect.right(), menubar_rect.bottom() - 1 }, palette.threed_shadow1());
painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, palette.threed_shadow2());
AppletManager::the().draw();
}
void MenuManager::refresh()
@ -266,8 +260,6 @@ void MenuManager::handle_mouse_event(MouseEvent& mouse_event)
}
return;
}
AppletManager::the().dispatch_event(static_cast<Event&>(mouse_event));
}
void MenuManager::set_needs_window_resize()

View File

@ -153,12 +153,13 @@ void Window::set_title(const String& title)
void Window::set_rect(const Gfx::IntRect& rect)
{
VERIFY(!rect.is_empty());
if (m_rect == rect)
return;
auto old_rect = m_rect;
m_rect = rect;
if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
if (rect.is_empty()) {
m_backing_store = nullptr;
} else if (!m_client && (!m_backing_store || old_rect.size() != rect.size())) {
m_backing_store = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRx8888, m_rect.size());
}

View File

@ -207,8 +207,8 @@ public:
Gfx::DisjointRectSet& dirty_rects() { return m_dirty_rects; }
// Only used by WindowType::MenuApplet. Perhaps it could be a Window subclass? I don't know.
void set_rect_in_menubar(const Gfx::IntRect& rect) { m_rect_in_menubar = rect; }
const Gfx::IntRect& rect_in_menubar() const { return m_rect_in_menubar; }
void set_rect_in_applet_area(const Gfx::IntRect& rect) { m_rect_in_applet_area = rect; }
const Gfx::IntRect& rect_in_applet_area() const { return m_rect_in_applet_area; }
const Gfx::Bitmap* backing_store() const { return m_backing_store.ptr(); }
Gfx::Bitmap* backing_store() { return m_backing_store.ptr(); }
@ -405,7 +405,7 @@ private:
unsigned m_wm_event_mask { 0 };
Gfx::DisjointRectSet m_pending_paint_rects;
Gfx::IntRect m_unmaximized_rect;
Gfx::IntRect m_rect_in_menubar;
Gfx::IntRect m_rect_in_applet_area;
RefPtr<Menu> m_window_menu;
MenuItem* m_window_menu_minimize_item { nullptr };
MenuItem* m_window_menu_maximize_item { nullptr };

View File

@ -26,6 +26,7 @@ endpoint WindowClient = 4
WM_WindowStateChanged(i32 wm_id, i32 client_id, i32 window_id, i32 parent_client_id, i32 parent_window_id, bool is_active, bool is_minimized, bool is_modal, bool is_frameless, i32 window_type, [UTF8] String title, Gfx::IntRect rect, i32 progress) =|
WM_WindowIconBitmapChanged(i32 wm_id, i32 client_id, i32 window_id, Gfx::ShareableBitmap bitmap) =|
WM_WindowRectChanged(i32 wm_id, i32 client_id, i32 window_id, Gfx::IntRect rect) =|
WM_AppletAreaSizeChanged(i32 wm_id, Gfx::IntSize size) =|
AsyncSetWallpaperFinished(bool success) =|

View File

@ -217,6 +217,8 @@ Gfx::Bitmap* WindowFrame::window_shadow() const
return s_menu_bar_shadow;
case WindowType::Taskbar:
return s_task_bar_shadow;
case WindowType::AppletArea:
return nullptr;
default:
if (auto* highlight_window = WindowManager::the().highlight_window())
return highlight_window == &m_window ? s_active_window_shadow : s_inactive_window_shadow;

View File

@ -204,6 +204,8 @@ void WindowManager::add_window(Window& window)
}
return IterationDecision::Continue;
});
if (auto* applet_area_window = AppletManager::the().window())
tell_wm_listeners_applet_area_size_changed(applet_area_window->size());
}
window.invalidate(true, true);
@ -341,6 +343,14 @@ void WindowManager::tell_wm_listeners_window_rect_changed(Window& window)
});
}
void WindowManager::tell_wm_listeners_applet_area_size_changed(const Gfx::IntSize& size)
{
for_each_window_listening_to_wm_events([&](Window& listener) {
listener.client()->post_message(Messages::WindowClient::WM_AppletAreaSizeChanged(listener.window_id(), size));
return IterationDecision::Continue;
});
}
static bool window_type_has_title(WindowType type)
{
return type == WindowType::Normal || type == WindowType::ToolWindow;
@ -382,7 +392,7 @@ void WindowManager::notify_rect_changed(Window& window, const Gfx::IntRect& old_
tell_wm_listeners_window_rect_changed(window);
if (window.type() == WindowType::MenuApplet)
AppletManager::the().calculate_applet_rects(MenuManager::the().window());
AppletManager::the().relayout();
MenuManager::the().refresh();
}
@ -931,7 +941,7 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
if (MenuManager::the().has_open_menu()
|| hitting_menu_in_window_with_active_menu) {
for_each_visible_window_of_type_from_front_to_back(WindowType::MenuApplet, [&](auto& window) {
if (!window.rect_in_menubar().contains(event.position()))
if (!window.rect_in_applet_area().contains(event.position()))
return IterationDecision::Continue;
hovered_window = &window;
return IterationDecision::Break;

View File

@ -167,6 +167,7 @@ public:
void tell_wm_listeners_window_state_changed(Window&);
void tell_wm_listeners_window_icon_changed(Window&);
void tell_wm_listeners_window_rect_changed(Window&);
void tell_wm_listeners_applet_area_size_changed(const Gfx::IntSize&);
bool is_active_window_or_accessory(Window&) const;
@ -397,6 +398,8 @@ IterationDecision WindowManager::for_each_visible_window_from_back_to_front(Call
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Taskbar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::AppletArea, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Notification, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_back_to_front(WindowType::Tooltip, callback) == IterationDecision::Break)
@ -444,6 +447,8 @@ IterationDecision WindowManager::for_each_visible_window_from_front_to_back(Call
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Notification, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::AppletArea, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::Taskbar, callback) == IterationDecision::Break)
return IterationDecision::Break;
if (for_each_visible_window_of_type_from_front_to_back(WindowType::ToolWindow, callback) == IterationDecision::Break)

View File

@ -83,6 +83,7 @@ endpoint WindowServer = 2
WM_StartWindowResize(i32 client_id, i32 window_id) =|
WM_PopupWindowMenu(i32 client_id, i32 window_id, Gfx::IntPoint screen_position) =|
WM_SetWindowTaskbarRect(i32 client_id, i32 window_id, Gfx::IntRect rect) =|
WM_SetAppletAreaPosition(Gfx::IntPoint position) => ()
SetWindowHasAlphaChannel(i32 window_id, bool has_alpha_channel) => ()
MoveWindowToFront(i32 window_id) => ()

View File

@ -40,6 +40,7 @@ enum class WindowType {
Notification,
Desktop,
ToolWindow,
AppletArea,
};
}