summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Applications/ThemeEditor/PreviewWidget.cpp4
-rw-r--r--Userland/Demos/LibGfxScaleDemo/main.cpp2
-rw-r--r--Userland/Libraries/LibGUI/Window.cpp10
-rw-r--r--Userland/Libraries/LibGUI/Window.h4
-rw-r--r--Userland/Libraries/LibGfx/ClassicWindowTheme.cpp23
-rw-r--r--Userland/Libraries/LibGfx/ClassicWindowTheme.h6
-rw-r--r--Userland/Libraries/LibGfx/WindowTheme.h6
-rw-r--r--Userland/Services/WindowServer/ClientConnection.cpp26
-rw-r--r--Userland/Services/WindowServer/ClientConnection.h12
-rw-r--r--Userland/Services/WindowServer/Menu.cpp2
-rw-r--r--Userland/Services/WindowServer/Menu.h18
-rw-r--r--Userland/Services/WindowServer/MenuBar.h8
-rw-r--r--Userland/Services/WindowServer/MenuManager.cpp43
-rw-r--r--Userland/Services/WindowServer/MenuManager.h6
-rw-r--r--Userland/Services/WindowServer/Window.cpp32
-rw-r--r--Userland/Services/WindowServer/Window.h8
-rw-r--r--Userland/Services/WindowServer/WindowFrame.cpp110
-rw-r--r--Userland/Services/WindowServer/WindowFrame.h11
-rw-r--r--Userland/Services/WindowServer/WindowManager.cpp41
-rw-r--r--Userland/Services/WindowServer/WindowManager.h5
-rw-r--r--Userland/Services/WindowServer/WindowServer.ipc2
21 files changed, 335 insertions, 44 deletions
diff --git a/Userland/Applications/ThemeEditor/PreviewWidget.cpp b/Userland/Applications/ThemeEditor/PreviewWidget.cpp
index a57da75936..fe4e74faf4 100644
--- a/Userland/Applications/ThemeEditor/PreviewWidget.cpp
+++ b/Userland/Applications/ThemeEditor/PreviewWidget.cpp
@@ -145,10 +145,10 @@ void PreviewWidget::paint_event(GUI::PaintEvent& event)
button.rect = rect;
}
- auto frame_rect = Gfx::WindowTheme::current().frame_rect_for_window(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette);
+ auto frame_rect = Gfx::WindowTheme::current().frame_rect_for_window(Gfx::WindowTheme::WindowType::Normal, rect, m_preview_palette, 0);
Gfx::PainterStateSaver saver(painter);
painter.translate(frame_rect.location());
- Gfx::WindowTheme::current().paint_normal_frame(painter, state, rect, title, icon, m_preview_palette, buttons.last().rect);
+ Gfx::WindowTheme::current().paint_normal_frame(painter, state, rect, title, icon, m_preview_palette, buttons.last().rect, 0);
for (auto& button : buttons) {
Gfx::StylePainter::paint_button(painter, button.rect, m_preview_palette, Gfx::ButtonStyle::Normal, false);
diff --git a/Userland/Demos/LibGfxScaleDemo/main.cpp b/Userland/Demos/LibGfxScaleDemo/main.cpp
index 4c2185d126..e6865c9def 100644
--- a/Userland/Demos/LibGfxScaleDemo/main.cpp
+++ b/Userland/Demos/LibGfxScaleDemo/main.cpp
@@ -93,7 +93,7 @@ void Canvas::paint_event(GUI::PaintEvent& event)
void Canvas::draw(Gfx::Painter& painter)
{
auto active_window_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/window.png");
- Gfx::WindowTheme::current().paint_normal_frame(painter, Gfx::WindowTheme::WindowState::Active, { 4, 18, WIDTH - 8, HEIGHT - 29 }, "Well hello friends 🐞", *active_window_icon, palette(), { WIDTH - 20, 6, 16, 16 });
+ Gfx::WindowTheme::current().paint_normal_frame(painter, Gfx::WindowTheme::WindowState::Active, { 4, 18, WIDTH - 8, HEIGHT - 29 }, "Well hello friends 🐞", *active_window_icon, palette(), { WIDTH - 20, 6, 16, 16 }, 0);
painter.draw_rect({ 20, 34, WIDTH - 40, HEIGHT - 45 }, palette().color(Gfx::ColorRole::Selection), true);
painter.draw_rect({ 24, 38, WIDTH - 48, HEIGHT - 53 }, palette().color(Gfx::ColorRole::Selection));
diff --git a/Userland/Libraries/LibGUI/Window.cpp b/Userland/Libraries/LibGUI/Window.cpp
index b375ac5d80..a7f3c79911 100644
--- a/Userland/Libraries/LibGUI/Window.cpp
+++ b/Userland/Libraries/LibGUI/Window.cpp
@@ -35,6 +35,7 @@
#include <LibGUI/Application.h>
#include <LibGUI/Desktop.h>
#include <LibGUI/Event.h>
+#include <LibGUI/MenuBar.h>
#include <LibGUI/Painter.h>
#include <LibGUI/Widget.h>
#include <LibGUI/Window.h>
@@ -1052,4 +1053,13 @@ Gfx::Bitmap* Window::back_bitmap()
return m_back_store ? &m_back_store->bitmap() : nullptr;
}
+void Window::set_menubar(RefPtr<MenuBar> menubar)
+{
+ if (m_menubar == menubar)
+ return;
+ m_menubar = move(menubar);
+ if (m_window_id && m_menubar)
+ WindowServerConnection::the().send_sync<Messages::WindowServer::SetWindowMenubar>(m_window_id, m_menubar->menubar_id());
+}
+
}
diff --git a/Userland/Libraries/LibGUI/Window.h b/Userland/Libraries/LibGUI/Window.h
index 516a07b100..306788d8e7 100644
--- a/Userland/Libraries/LibGUI/Window.h
+++ b/Userland/Libraries/LibGUI/Window.h
@@ -211,6 +211,8 @@ public:
void did_disable_focused_widget(Badge<Widget>);
+ void set_menubar(RefPtr<MenuBar>);
+
protected:
Window(Core::Object* parent = nullptr);
virtual void wm_event(WMEvent&);
@@ -241,6 +243,8 @@ private:
OwnPtr<WindowBackingStore> m_front_store;
OwnPtr<WindowBackingStore> m_back_store;
+ RefPtr<MenuBar> m_menubar;
+
RefPtr<Gfx::Bitmap> m_icon;
RefPtr<Gfx::Bitmap> m_custom_cursor;
int m_window_id { 0 };
diff --git a/Userland/Libraries/LibGfx/ClassicWindowTheme.cpp b/Userland/Libraries/LibGfx/ClassicWindowTheme.cpp
index 71ab889c31..8b9d8f9609 100644
--- a/Userland/Libraries/LibGfx/ClassicWindowTheme.cpp
+++ b/Userland/Libraries/LibGfx/ClassicWindowTheme.cpp
@@ -33,6 +33,8 @@
namespace Gfx {
+static constexpr int menu_bar_height = 19;
+
ClassicWindowTheme::ClassicWindowTheme()
{
}
@@ -70,9 +72,9 @@ Gfx::IntRect ClassicWindowTheme::title_bar_text_rect(WindowType window_type, con
};
}
-void ClassicWindowTheme::paint_normal_frame(Painter& painter, WindowState window_state, const IntRect& window_rect, const StringView& title_text, const Bitmap& icon, const Palette& palette, const IntRect& leftmost_button_rect) const
+void ClassicWindowTheme::paint_normal_frame(Painter& painter, WindowState window_state, const IntRect& window_rect, const StringView& title_text, const Bitmap& icon, const Palette& palette, const IntRect& leftmost_button_rect, int menu_row_count) const
{
- auto frame_rect = frame_rect_for_window(WindowType::Normal, window_rect, palette);
+ auto frame_rect = frame_rect_for_window(WindowType::Normal, window_rect, palette, menu_row_count);
frame_rect.set_location({ 0, 0 });
Gfx::StylePainter::paint_window_frame(painter, frame_rect, palette);
@@ -114,7 +116,7 @@ void ClassicWindowTheme::paint_normal_frame(Painter& painter, WindowState window
void ClassicWindowTheme::paint_tool_window_frame(Painter& painter, WindowState window_state, const IntRect& window_rect, const StringView& title_text, const Palette& palette, const IntRect& leftmost_button_rect) const
{
- auto frame_rect = frame_rect_for_window(WindowType::ToolWindow, window_rect, palette);
+ auto frame_rect = frame_rect_for_window(WindowType::ToolWindow, window_rect, palette, 0);
frame_rect.set_location({ 0, 0 });
Gfx::StylePainter::paint_window_frame(painter, frame_rect, palette);
@@ -143,6 +145,13 @@ void ClassicWindowTheme::paint_tool_window_frame(Painter& painter, WindowState w
}
}
+IntRect ClassicWindowTheme::menu_bar_rect(WindowType window_type, const IntRect& window_rect, const Palette& palette, int menu_row_count) const
+{
+ if (window_type != WindowType::Normal)
+ return {};
+ return { 4, 4 + title_bar_height(window_type, palette) + 2, window_rect.width(), menu_bar_height * menu_row_count };
+}
+
IntRect ClassicWindowTheme::title_bar_rect(WindowType window_type, const IntRect& window_rect, const Palette& palette) const
{
auto& title_font = FontDatabase::default_bold_font();
@@ -173,7 +182,7 @@ ClassicWindowTheme::FrameColors ClassicWindowTheme::compute_frame_colors(WindowS
void ClassicWindowTheme::paint_notification_frame(Painter& painter, const IntRect& window_rect, const Palette& palette, const IntRect& close_button_rect) const
{
- auto frame_rect = frame_rect_for_window(WindowType::Notification, window_rect, palette);
+ auto frame_rect = frame_rect_for_window(WindowType::Notification, window_rect, palette, 0);
frame_rect.set_location({ 0, 0 });
Gfx::StylePainter::paint_window_frame(painter, frame_rect, palette);
@@ -191,7 +200,7 @@ void ClassicWindowTheme::paint_notification_frame(Painter& painter, const IntRec
}
}
-IntRect ClassicWindowTheme::frame_rect_for_window(WindowType window_type, const IntRect& window_rect, const Gfx::Palette& palette) const
+IntRect ClassicWindowTheme::frame_rect_for_window(WindowType window_type, const IntRect& window_rect, const Gfx::Palette& palette, int menu_row_count) const
{
auto window_titlebar_height = title_bar_height(window_type, palette);
@@ -200,9 +209,9 @@ IntRect ClassicWindowTheme::frame_rect_for_window(WindowType window_type, const
case WindowType::ToolWindow:
return {
window_rect.x() - 4,
- window_rect.y() - window_titlebar_height - 6,
+ window_rect.y() - window_titlebar_height - 6 - menu_row_count * menu_bar_height,
window_rect.width() + 8,
- window_rect.height() + 10 + window_titlebar_height
+ window_rect.height() + 10 + window_titlebar_height + menu_row_count * menu_bar_height
};
case WindowType::Notification:
return {
diff --git a/Userland/Libraries/LibGfx/ClassicWindowTheme.h b/Userland/Libraries/LibGfx/ClassicWindowTheme.h
index ae5b633e1d..58015b4e24 100644
--- a/Userland/Libraries/LibGfx/ClassicWindowTheme.h
+++ b/Userland/Libraries/LibGfx/ClassicWindowTheme.h
@@ -36,7 +36,7 @@ public:
ClassicWindowTheme();
virtual ~ClassicWindowTheme() override;
- virtual void paint_normal_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Bitmap& icon, const Palette&, const IntRect& leftmost_button_rect) const override;
+ virtual void paint_normal_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Bitmap& icon, const Palette&, const IntRect& leftmost_button_rect, int menu_row_count) const override;
virtual void paint_tool_window_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Palette&, const IntRect& leftmost_button_rect) const override;
virtual void paint_notification_frame(Painter&, const IntRect& window_rect, const Palette&, const IntRect& close_button_rect) const override;
@@ -45,7 +45,9 @@ public:
virtual IntRect title_bar_icon_rect(WindowType, const IntRect& window_rect, const Palette&) const override;
virtual IntRect title_bar_text_rect(WindowType, const IntRect& window_rect, const Palette&) const override;
- virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const override;
+ virtual IntRect menu_bar_rect(WindowType, const IntRect& window_rect, const Palette&, int menu_row_count) const override;
+
+ virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&, int menu_row_count) const override;
virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const override;
virtual bool is_simple_rect_frame() const override { return true; }
diff --git a/Userland/Libraries/LibGfx/WindowTheme.h b/Userland/Libraries/LibGfx/WindowTheme.h
index 2231187feb..9e60137f36 100644
--- a/Userland/Libraries/LibGfx/WindowTheme.h
+++ b/Userland/Libraries/LibGfx/WindowTheme.h
@@ -51,7 +51,7 @@ public:
static WindowTheme& current();
- virtual void paint_normal_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Bitmap& icon, const Palette&, const IntRect& leftmost_button_rect) const = 0;
+ virtual void paint_normal_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Bitmap& icon, const Palette&, const IntRect& leftmost_button_rect, int menu_row_count) const = 0;
virtual void paint_tool_window_frame(Painter&, WindowState, const IntRect& window_rect, const StringView& title, const Palette&, const IntRect& leftmost_button_rect) const = 0;
virtual void paint_notification_frame(Painter&, const IntRect& window_rect, const Palette&, const IntRect& close_button_rect) const = 0;
@@ -60,7 +60,9 @@ public:
virtual IntRect title_bar_icon_rect(WindowType, const IntRect& window_rect, const Palette&) const = 0;
virtual IntRect title_bar_text_rect(WindowType, const IntRect& window_rect, const Palette&) const = 0;
- virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&) const = 0;
+ virtual IntRect menu_bar_rect(WindowType, const IntRect& window_rect, const Palette&, int menu_row_count) const = 0;
+
+ virtual IntRect frame_rect_for_window(WindowType, const IntRect& window_rect, const Palette&, int menu_row_count) const = 0;
virtual Vector<IntRect> layout_buttons(WindowType, const IntRect& window_rect, const Palette&, size_t buttons) const = 0;
virtual bool is_simple_rect_frame() const = 0;
diff --git a/Userland/Services/WindowServer/ClientConnection.cpp b/Userland/Services/WindowServer/ClientConnection.cpp
index 243b2a1056..db2f911043 100644
--- a/Userland/Services/WindowServer/ClientConnection.cpp
+++ b/Userland/Services/WindowServer/ClientConnection.cpp
@@ -104,7 +104,7 @@ void ClientConnection::notify_about_new_screen_rect(const Gfx::IntRect& rect)
OwnPtr<Messages::WindowServer::CreateMenubarResponse> ClientConnection::handle(const Messages::WindowServer::CreateMenubar&)
{
int menubar_id = m_next_menubar_id++;
- auto menubar = make<MenuBar>(*this, menubar_id);
+ auto menubar = MenuBar::create(*this, menubar_id);
m_menubars.set(menubar_id, move(menubar));
return make<Messages::WindowServer::CreateMenubarResponse>(menubar_id);
}
@@ -160,6 +160,30 @@ OwnPtr<Messages::WindowServer::SetApplicationMenubarResponse> ClientConnection::
return make<Messages::WindowServer::SetApplicationMenubarResponse>();
}
+OwnPtr<Messages::WindowServer::SetWindowMenubarResponse> ClientConnection::handle(const Messages::WindowServer::SetWindowMenubar& message)
+{
+ RefPtr<Window> window;
+ {
+ auto it = m_windows.find(message.window_id());
+ if (it == m_windows.end()) {
+ did_misbehave("SetWindowMenubar: Bad window ID");
+ return {};
+ }
+ window = it->value;
+ }
+ RefPtr<MenuBar> menubar;
+ if (message.menubar_id() != -1) {
+ auto it = m_menubars.find(message.menubar_id());
+ if (it == m_menubars.end()) {
+ did_misbehave("SetWindowMenubar: Bad menubar ID");
+ return {};
+ }
+ menubar = *(*it).value;
+ }
+ window->set_menubar(menubar);
+ return make<Messages::WindowServer::SetWindowMenubarResponse>();
+}
+
OwnPtr<Messages::WindowServer::AddMenuToMenubarResponse> ClientConnection::handle(const Messages::WindowServer::AddMenuToMenubar& message)
{
int menubar_id = message.menubar_id();
diff --git a/Userland/Services/WindowServer/ClientConnection.h b/Userland/Services/WindowServer/ClientConnection.h
index 9b4e4d7742..497b2b4505 100644
--- a/Userland/Services/WindowServer/ClientConnection.h
+++ b/Userland/Services/WindowServer/ClientConnection.h
@@ -78,6 +78,15 @@ public:
return menu.value().ptr();
}
+ template<typename Callback>
+ void for_each_window(Callback callback)
+ {
+ for (auto& it : m_windows) {
+ if (callback(*it.value) == IterationDecision::Break)
+ break;
+ }
+ }
+
void notify_display_link(Badge<Compositor>);
private:
@@ -98,6 +107,7 @@ private:
virtual OwnPtr<Messages::WindowServer::DestroyMenuResponse> handle(const Messages::WindowServer::DestroyMenu&) override;
virtual OwnPtr<Messages::WindowServer::AddMenuToMenubarResponse> handle(const Messages::WindowServer::AddMenuToMenubar&) override;
virtual OwnPtr<Messages::WindowServer::SetApplicationMenubarResponse> handle(const Messages::WindowServer::SetApplicationMenubar&) override;
+ virtual OwnPtr<Messages::WindowServer::SetWindowMenubarResponse> handle(const Messages::WindowServer::SetWindowMenubar&) override;
virtual OwnPtr<Messages::WindowServer::AddMenuItemResponse> handle(const Messages::WindowServer::AddMenuItem&) override;
virtual OwnPtr<Messages::WindowServer::AddMenuSeparatorResponse> handle(const Messages::WindowServer::AddMenuSeparator&) override;
virtual OwnPtr<Messages::WindowServer::UpdateMenuItemResponse> handle(const Messages::WindowServer::UpdateMenuItem&) override;
@@ -157,7 +167,7 @@ private:
Window* window_from_id(i32 window_id);
HashMap<int, NonnullRefPtr<Window>> m_windows;
- HashMap<int, NonnullOwnPtr<MenuBar>> m_menubars;
+ HashMap<int, NonnullRefPtr<MenuBar>> m_menubars;
HashMap<int, NonnullRefPtr<Menu>> m_menus;
WeakPtr<MenuBar> m_app_menubar;
diff --git a/Userland/Services/WindowServer/Menu.cpp b/Userland/Services/WindowServer/Menu.cpp
index 3cee99bc05..0096571b2f 100644
--- a/Userland/Services/WindowServer/Menu.cpp
+++ b/Userland/Services/WindowServer/Menu.cpp
@@ -122,7 +122,7 @@ int Menu::content_width() const
if (widest_shortcut)
widest_item += padding_between_text_and_shortcut() + widest_shortcut;
- return max(widest_item, rect_in_menubar().width()) + horizontal_padding() + frame_thickness() * 2;
+ return max(widest_item, rect_in_global_menubar().width()) + horizontal_padding() + frame_thickness() * 2;
}
void Menu::redraw()
diff --git a/Userland/Services/WindowServer/Menu.h b/Userland/Services/WindowServer/Menu.h
index 20fdb201cc..670c412f5b 100644
--- a/Userland/Services/WindowServer/Menu.h
+++ b/Userland/Services/WindowServer/Menu.h
@@ -74,11 +74,15 @@ public:
callback(item);
}
- Gfx::IntRect text_rect_in_menubar() const { return m_text_rect_in_menubar; }
- void set_text_rect_in_menubar(const Gfx::IntRect& rect) { m_text_rect_in_menubar = rect; }
+ Gfx::IntRect text_rect_in_global_menubar() const { return m_text_rect_in_global_menubar; }
+ void set_text_rect_in_global_menubar(const Gfx::IntRect& rect) { m_text_rect_in_global_menubar = rect; }
+ Gfx::IntRect rect_in_global_menubar() const { return m_rect_in_global_menubar; }
+ void set_rect_in_global_menubar(const Gfx::IntRect& rect) { m_rect_in_global_menubar = rect; }
- Gfx::IntRect rect_in_menubar() const { return m_rect_in_menubar; }
- void set_rect_in_menubar(const Gfx::IntRect& rect) { m_rect_in_menubar = rect; }
+ Gfx::IntRect text_rect_in_window_menubar() const { return m_text_rect_in_window_menubar; }
+ void set_text_rect_in_window_menubar(const Gfx::IntRect& rect) { m_text_rect_in_window_menubar = rect; }
+ Gfx::IntRect rect_in_window_menubar() const { return m_rect_in_window_menubar; }
+ void set_rect_in_window_menubar(const Gfx::IntRect& rect) { m_rect_in_window_menubar = rect; }
Window* menu_window() { return m_menu_window.ptr(); }
Window& ensure_menu_window();
@@ -149,8 +153,10 @@ private:
ClientConnection* m_client { nullptr };
int m_menu_id { 0 };
String m_name;
- Gfx::IntRect m_rect_in_menubar;
- Gfx::IntRect m_text_rect_in_menubar;
+ Gfx::IntRect m_rect_in_global_menubar;
+ Gfx::IntRect m_text_rect_in_global_menubar;
+ Gfx::IntRect m_rect_in_window_menubar;
+ Gfx::IntRect m_text_rect_in_window_menubar;
MenuBar* m_menubar { nullptr };
NonnullOwnPtrVector<MenuItem> m_items;
RefPtr<Window> m_menu_window;
diff --git a/Userland/Services/WindowServer/MenuBar.h b/Userland/Services/WindowServer/MenuBar.h
index 31dff9ac0f..4008b8e722 100644
--- a/Userland/Services/WindowServer/MenuBar.h
+++ b/Userland/Services/WindowServer/MenuBar.h
@@ -33,9 +33,11 @@
namespace WindowServer {
-class MenuBar : public Weakable<MenuBar> {
+class MenuBar
+ : public RefCounted<MenuBar>
+ , public Weakable<MenuBar> {
public:
- MenuBar(ClientConnection& client, int menubar_id);
+ static NonnullRefPtr<MenuBar> create(ClientConnection& client, int menubar_id) { return adopt(*new MenuBar(client, menubar_id)); }
~MenuBar();
ClientConnection& client() { return m_client; }
@@ -53,6 +55,8 @@ public:
}
private:
+ MenuBar(ClientConnection&, int menubar_id);
+
ClientConnection& m_client;
int m_menubar_id { 0 };
Vector<Menu*> m_menus;
diff --git a/Userland/Services/WindowServer/MenuManager.cpp b/Userland/Services/WindowServer/MenuManager.cpp
index 214e055fcc..8fdc5cc6b8 100644
--- a/Userland/Services/WindowServer/MenuManager.cpp
+++ b/Userland/Services/WindowServer/MenuManager.cpp
@@ -32,6 +32,7 @@
#include <LibGfx/Font.h>
#include <LibGfx/Painter.h>
#include <WindowServer/AppletManager.h>
+#include <WindowServer/ClientConnection.h>
#include <WindowServer/MenuManager.h>
#include <WindowServer/Screen.h>
#include <WindowServer/WindowManager.h>
@@ -96,19 +97,20 @@ void MenuManager::draw()
painter.draw_line({ 0, menubar_rect.bottom() }, { menubar_rect.right(), menubar_rect.bottom() }, palette.threed_shadow2());
for_each_active_menubar_menu([&](Menu& menu) {
+ auto text_rect = menu.text_rect_in_global_menubar();
Color text_color = palette.window_text();
if (is_open(menu) && &menu == m_current_menu_bar_menu) {
- painter.fill_rect(menu.rect_in_menubar(), palette.menu_selection());
- painter.draw_rect(menu.rect_in_menubar(), palette.menu_selection().darkened());
+ painter.fill_rect(menu.rect_in_global_menubar(), palette.menu_selection());
+ painter.draw_rect(menu.rect_in_global_menubar(), palette.menu_selection().darkened());
text_color = palette.menu_selection_text();
+ text_rect.move_by(1, 1);
}
painter.draw_text(
- menu.text_rect_in_menubar(),
+ text_rect,
menu.name(),
menu.title_font(),
Gfx::TextAlignment::CenterLeft,
text_color);
- //painter.draw_rect(menu.text_rect_in_menubar(), Color::Magenta);
return IterationDecision::Continue;
});
@@ -216,7 +218,7 @@ void MenuManager::handle_mouse_event(MouseEvent& mouse_event)
auto* active_window = WindowManager::the().active_window();
bool handled_menubar_event = false;
for_each_active_menubar_menu([&](Menu& menu) {
- if (menu.rect_in_menubar().contains(mouse_event.position())) {
+ if (menu.rect_in_global_menubar().contains(mouse_event.position())) {
handled_menubar_event = &menu == m_system_menu || !active_window || !active_window->is_modal();
if (handled_menubar_event)
handle_menu_mouse_event(menu, mouse_event);
@@ -383,6 +385,25 @@ void MenuManager::close_menu_and_descendants(Menu& menu)
close_menus(menus_to_close);
}
+void MenuManager::set_hovered_menu(Menu* menu)
+{
+ if (m_hovered_menu == menu)
+ return;
+ if (menu) {
+ m_hovered_menu = menu->make_weak_ptr<Menu>();
+ } else {
+ // FIXME: This is quite aggressive. If we knew which window the previously hovered menu was in,
+ // we could just invalidate that one instead of iterating all windows in the client.
+ if (auto* client = m_hovered_menu->client()) {
+ client->for_each_window([&](Window& window) {
+ window.invalidate_menubar();
+ return IterationDecision::Continue;
+ });
+ }
+ m_hovered_menu = nullptr;
+ }
+}
+
void MenuManager::open_menu(Menu& menu, bool from_menu_bar, bool as_current_menu)
{
if (from_menu_bar)
@@ -405,7 +426,7 @@ void MenuManager::open_menu(Menu& menu, bool from_menu_bar, bool as_current_menu
menu.ensure_menu_window();
}
if (should_update_position)
- menu.menu_window()->move_to({ menu.rect_in_menubar().x(), menu.rect_in_menubar().bottom() + 2 });
+ menu.menu_window()->move_to({ menu.rect_in_global_menubar().x(), menu.rect_in_global_menubar().bottom() + 2 });
menu.menu_window()->set_visible(true);
}
@@ -430,6 +451,10 @@ void MenuManager::clear_current_menu()
// When closing the last menu, restore the previous active input window
auto& wm = WindowManager::the();
wm.restore_active_input_window(m_previous_input_window);
+ if (auto* window = wm.window_with_active_menu()) {
+ window->invalidate_menubar();
+ }
+ wm.set_window_with_active_menu(nullptr);
}
}
@@ -485,12 +510,12 @@ void MenuManager::set_current_menubar(MenuBar* menubar)
Gfx::IntPoint next_menu_location { MenuManager::menubar_menu_margin() / 2, 0 };
for_each_active_menubar_menu([&](Menu& menu) {
int text_width = menu.title_font().width(menu.name());
- menu.set_rect_in_menubar({ next_menu_location.x() - MenuManager::menubar_menu_margin() / 2, 0, text_width + MenuManager::menubar_menu_margin(), menubar_rect().height() - 1 });
+ menu.set_rect_in_global_menubar({ next_menu_location.x() - MenuManager::menubar_menu_margin() / 2, 0, text_width + MenuManager::menubar_menu_margin(), menubar_rect().height() - 1 });
Gfx::IntRect text_rect { next_menu_location.translated(0, 1), { text_width, menubar_rect().height() - 3 } };
- menu.set_text_rect_in_menubar(text_rect);
- next_menu_location.move_by(menu.rect_in_menubar().width(), 0);
+ menu.set_text_rect_in_global_menubar(text_rect);
+ next_menu_location.move_by(menu.rect_in_global_menubar().width(), 0);
return IterationDecision::Continue;
});
refresh();
diff --git a/Userland/Services/WindowServer/MenuManager.h b/Userland/Services/WindowServer/MenuManager.h
index 8fcbf1ffe8..1ca18dd100 100644
--- a/Userland/Services/WindowServer/MenuManager.h
+++ b/Userland/Services/WindowServer/MenuManager.h
@@ -50,7 +50,7 @@ public:
bool has_open_menu() const { return !m_open_menu_stack.is_empty(); }
Gfx::IntRect menubar_rect() const;
- static int menubar_menu_margin() { return 16; }
+ static int menubar_menu_margin() { return 13; }
void set_needs_window_resize();
@@ -93,6 +93,9 @@ public:
void did_change_theme();
+ void set_hovered_menu(Menu*);
+ Menu* hovered_menu() { return m_hovered_menu; }
+
private:
void close_menus(const Vector<Menu*>&);
@@ -121,6 +124,7 @@ private:
int m_theme_index { 0 };
WeakPtr<MenuBar> m_current_menubar;
+ WeakPtr<Menu> m_hovered_menu;
};
}
diff --git a/Userland/Services/WindowServer/Window.cpp b/Userland/Services/WindowServer/Window.cpp
index 51ccd784a5..d0cecd3585 100644
--- a/Userland/Services/WindowServer/Window.cpp
+++ b/Userland/Services/WindowServer/Window.cpp
@@ -942,4 +942,36 @@ bool Window::hit_test(const Gfx::IntPoint& point, bool include_frame) const
return color.alpha() >= threshold;
}
+void Window::set_menubar(MenuBar* menubar)
+{
+ if (m_menubar == menubar)
+ return;
+ m_menubar = menubar;
+ if (m_menubar) {
+ auto& wm = WindowManager::the();
+ Gfx::IntPoint next_menu_location { MenuManager::menubar_menu_margin() / 2, 0 };
+ auto menubar_rect = Gfx::WindowTheme::current().menu_bar_rect(Gfx::WindowTheme::WindowType::Normal, rect(), wm.palette(), 1);
+ m_menubar->for_each_menu([&](Menu& menu) {
+ int text_width = wm.font().width(menu.name());
+ menu.set_rect_in_window_menubar({ next_menu_location.x() - MenuManager::menubar_menu_margin() / 2, 0, text_width + MenuManager::menubar_menu_margin(), menubar_rect.height() });
+
+ Gfx::IntRect text_rect { next_menu_location.translated(0, 1), { text_width, menubar_rect.height() - 3 } };
+
+ menu.set_text_rect_in_window_menubar(text_rect);
+ next_menu_location.move_by(menu.rect_in_window_menubar().width(), 0);
+ return IterationDecision::Continue;
+ });
+ }
+ Compositor::the().invalidate_occlusions();
+ frame().invalidate();
+}
+
+void Window::invalidate_menubar()
+{
+ if (!menubar())
+ return;
+ // FIXME: This invalidates way more than the menubar!
+ frame().invalidate();
+}
+
}
diff --git a/Userland/Services/WindowServer/Window.h b/Userland/Services/WindowServer/Window.h
index 7d5891196e..f62b07f871 100644
--- a/Userland/Services/WindowServer/Window.h
+++ b/Userland/Services/WindowServer/Window.h
@@ -42,6 +42,7 @@ namespace WindowServer {
class ClientConnection;
class Cursor;
class Menu;
+class MenuBar;
class MenuItem;
class MouseEvent;
@@ -192,6 +193,7 @@ public:
void invalidate(bool with_frame = true, bool re_render_frame = false);
void invalidate(const Gfx::IntRect&, bool with_frame = false);
+ void invalidate_menubar();
bool invalidate_no_notify(const Gfx::IntRect& rect, bool with_frame = false);
void refresh_client_size();
@@ -326,6 +328,10 @@ public:
Gfx::DisjointRectSet& transparency_rects() { return m_transparency_rects; }
Gfx::DisjointRectSet& transparency_wallpaper_rects() { return m_transparency_wallpaper_rects; }
+ MenuBar* menubar() { return m_menubar; }
+ const MenuBar* menubar() const { return m_menubar; }
+ void set_menubar(MenuBar*);
+
private:
void handle_mouse_event(const MouseEvent&);
void update_menu_item_text(PopupMenuItem item);
@@ -341,6 +347,8 @@ private:
Vector<WeakPtr<Window>> m_child_windows;
Vector<WeakPtr<Window>> m_accessory_windows;
+ RefPtr<MenuBar> m_menubar;
+
String m_title;
Gfx::IntRect m_rect;
Gfx::IntRect m_saved_nonfullscreen_rect;
diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp
index 8ada69986b..257747f21d 100644
--- a/Userland/Services/WindowServer/WindowFrame.cpp
+++ b/Userland/Services/WindowServer/WindowFrame.cpp
@@ -79,7 +79,8 @@ static Gfx::IntRect frame_rect_for_window(Window& window, const Gfx::IntRect& re
{
if (window.is_frameless())
return rect;
- return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette());
+ int menu_row_count = window.menubar() ? 1 : 0;
+ return Gfx::WindowTheme::current().frame_rect_for_window(to_theme_window_type(window.type()), rect, WindowManager::the().palette(), menu_row_count);
}
WindowFrame::WindowFrame(Window& window)
@@ -238,6 +239,13 @@ void WindowFrame::did_set_maximized(Badge<Window>, bool maximized)
m_maximize_button->set_icon(maximized ? *s_restore_icon : *s_maximize_icon);
}
+Gfx::IntRect WindowFrame::menubar_rect() const
+{
+ if (!m_window.menubar())
+ return {};
+ return Gfx::WindowTheme::current().menu_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette(), menu_row_count());
+}
+
Gfx::IntRect WindowFrame::title_bar_rect() const
{
return Gfx::WindowTheme::current().title_bar_rect(to_theme_window_type(m_window.type()), m_window.rect(), WindowManager::the().palette());
@@ -289,11 +297,46 @@ void WindowFrame::paint_tool_window_frame(Gfx::Painter& painter)
Gfx::WindowTheme::current().paint_tool_window_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), palette, leftmost_button_rect);
}
+void WindowFrame::paint_menubar(Gfx::Painter& painter)
+{
+ auto& wm = WindowManager::the();
+ auto& font = wm.font();
+ auto palette = wm.palette();
+ auto menubar_rect = this->menubar_rect();
+
+ painter.fill_rect(menubar_rect, palette.window());
+
+ Gfx::PainterStateSaver saver(painter);
+ painter.translate(menubar_rect.location());
+
+ m_window.menubar()->for_each_menu([&](Menu& menu) {
+ auto text_rect = menu.text_rect_in_window_menubar();
+ Color text_color = palette.window_text();
+ if (MenuManager::the().is_open(menu)) {
+ painter.fill_rect(menu.rect_in_window_menubar(), palette.menu_selection());
+ text_color = palette.menu_selection_text();
+ text_rect.move_by(1, 1);
+ }
+ if (&menu == MenuManager::the().hovered_menu() || MenuManager::the().is_open(menu))
+ Gfx::StylePainter::paint_button(painter, menu.rect_in_window_menubar(), palette, Gfx::ButtonStyle::CoolBar, MenuManager::the().is_open(menu), true);
+ painter.draw_text(
+ text_rect,
+ menu.name(),
+ font,
+ Gfx::TextAlignment::CenterLeft,
+ text_color);
+ return IterationDecision::Continue;
+ });
+}
+
void WindowFrame::paint_normal_frame(Gfx::Painter& painter)
{
auto palette = WindowManager::the().palette();
auto leftmost_button_rect = m_buttons.is_empty() ? Gfx::IntRect() : m_buttons.last().relative_rect();
- Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), m_window.icon(), palette, leftmost_button_rect);
+ Gfx::WindowTheme::current().paint_normal_frame(painter, window_state_for_theme(), m_window.rect(), compute_title_text(), m_window.icon(), palette, leftmost_button_rect, menu_row_count());
+
+ if (m_window.menubar())
+ paint_menubar(painter);
}
void WindowFrame::paint(Gfx::Painter& painter, const Gfx::IntRect& rect)
@@ -509,6 +552,13 @@ void WindowFrame::invalidate_title_bar()
invalidate(title_bar_rect());
}
+void WindowFrame::invalidate()
+{
+ auto frame_rect = render_rect();
+ invalidate(Gfx::IntRect { frame_rect.location() - m_window.position(), frame_rect.size() });
+ m_window.invalidate(true, true);
+}
+
void WindowFrame::invalidate(Gfx::IntRect relative_rect)
{
auto frame_rect = rect();
@@ -652,6 +702,13 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
return;
}
+ auto menubar_rect = this->menubar_rect();
+ if (menubar_rect.contains(event.position())) {
+ wm.clear_resize_candidate();
+ handle_menubar_mouse_event(event);
+ return;
+ }
+
if (m_window.is_resizable() && event.type() == Event::MouseMove && event.buttons() == 0) {
constexpr ResizeDirection direction_for_hot_area[3][3] = {
{ ResizeDirection::UpLeft, ResizeDirection::Up, ResizeDirection::UpRight },
@@ -673,6 +730,50 @@ void WindowFrame::on_mouse_event(const MouseEvent& event)
wm.start_window_resize(m_window, event.translated(rect().location()));
}
+void WindowFrame::handle_menubar_mouse_event(const MouseEvent& event)
+{
+ Menu* hovered_menu = nullptr;
+ auto menubar_rect = this->menubar_rect();
+ auto adjusted_position = event.position().translated(-menubar_rect.location());
+ m_window.menubar()->for_each_menu([&](Menu& menu) {
+ if (menu.rect_in_window_menubar().contains(adjusted_position)) {
+ hovered_menu = &menu;
+ handle_menu_mouse_event(menu, event);
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ if (!hovered_menu && event.type() == Event::Type::MouseDown)
+ MenuManager::the().close_everyone();
+ if (hovered_menu != MenuManager::the().hovered_menu()) {
+ MenuManager::the().set_hovered_menu(hovered_menu);
+ invalidate(menubar_rect);
+ }
+}
+
+void WindowFrame::handle_menu_mouse_event(Menu& menu, const MouseEvent& event)
+{
+ auto menubar_rect = this->menubar_rect();
+ bool is_hover_with_any_menu_open = event.type() == MouseEvent::MouseMove && &m_window == WindowManager::the().window_with_active_menu();
+ bool is_mousedown_with_left_button = event.type() == MouseEvent::MouseDown && event.button() == MouseButton::Left;
+ bool should_open_menu = &menu != MenuManager::the().current_menu() && (is_hover_with_any_menu_open || is_mousedown_with_left_button);
+ bool should_close_menu = &menu == MenuManager::the().current_menu() && is_mousedown_with_left_button;
+
+ if (should_open_menu) {
+ MenuManager::the().close_everyone();
+ menu.ensure_menu_window().move_to(menu.rect_in_window_menubar().bottom_left().translated(rect().location()).translated(menubar_rect.location()));
+ MenuManager::the().open_menu(menu, false);
+ WindowManager::the().set_window_with_active_menu(&m_window);
+ invalidate(menubar_rect);
+ return;
+ }
+
+ if (should_close_menu) {
+ invalidate(menubar_rect);
+ MenuManager::the().close_everyone();
+ }
+}
+
void WindowFrame::start_flash_animation()
{
if (!m_flash_timer) {
@@ -774,4 +875,9 @@ void WindowFrame::paint_simple_rect_shadow(Gfx::Painter& painter, const Gfx::Int
paint_vertical(containing_rect.right() - base_size + 1, 1, 0, horizontal_shift);
}
+int WindowFrame::menu_row_count() const
+{
+ return m_window.menubar() ? 1 : 0;
+}
+
}
diff --git a/Userland/Services/WindowServer/WindowFrame.h b/Userland/Services/WindowServer/WindowFrame.h
index 9ca67bf5ab..b127c95580 100644
--- a/Userland/Services/WindowServer/WindowFrame.h
+++ b/Userland/Services/WindowServer/WindowFrame.h
@@ -36,6 +36,7 @@
namespace WindowServer {
class Button;
+class Menu;
class MouseEvent;
class Window;
@@ -43,7 +44,7 @@ class WindowFrame {
public:
static void reload_config();
- WindowFrame(Window&);
+ explicit WindowFrame(Window&);
~WindowFrame();
Gfx::IntRect rect() const;
@@ -55,11 +56,15 @@ public:
void notify_window_rect_changed(const Gfx::IntRect& old_rect, const Gfx::IntRect& new_rect);
void invalidate_title_bar();
void invalidate(Gfx::IntRect relative_rect);
+ void invalidate();
Gfx::IntRect title_bar_rect() const;
Gfx::IntRect title_bar_icon_rect() const;
Gfx::IntRect title_bar_text_rect() const;
+ Gfx::IntRect menubar_rect() const;
+ int menu_row_count() const;
+
void did_set_maximized(Badge<Window>, bool);
void layout_buttons();
@@ -97,11 +102,15 @@ private:
void paint_notification_frame(Gfx::Painter&);
void paint_normal_frame(Gfx::Painter&);
void paint_tool_window_frame(Gfx::Painter&);
+ void paint_menubar(Gfx::Painter&);
Gfx::Bitmap* window_shadow() const;
bool frame_has_alpha() const;
Gfx::IntRect inflated_for_shadow(const Gfx::IntRect&) const;
Gfx::Bitmap* inflate_for_shadow(Gfx::IntRect&, Gfx::IntPoint&) const;
+ void handle_menubar_mouse_event(const MouseEvent&);
+ void handle_menu_mouse_event(Menu&, const MouseEvent&);
+
Gfx::WindowTheme::WindowState window_state_for_theme() const;
String compute_title_text() const;
diff --git a/Userland/Services/WindowServer/WindowManager.cpp b/Userland/Services/WindowServer/WindowManager.cpp
index 1a9e46dee4..14f108e52e 100644
--- a/Userland/Services/WindowServer/WindowManager.cpp
+++ b/Userland/Services/WindowServer/WindowManager.cpp
@@ -206,6 +206,8 @@ void WindowManager::add_window(Window& window)
});
}
+ window.invalidate(true, true);
+
tell_wm_listeners_window_state_changed(window);
}
@@ -914,8 +916,21 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
if (m_hovered_button && event.type() == Event::MouseMove)
m_hovered_button->on_mouse_event(event.translated(-m_hovered_button->screen_rect().location()));
- // FIXME: Now that the menubar has a dedicated window, is this special-casing really necessary?
- if (MenuManager::the().has_open_menu() || menubar_rect().contains(event.position())) {
+ bool hitting_menu_in_window_with_active_menu = [&] {
+ if (!m_window_with_active_menu)
+ return false;
+ auto& frame = m_window_with_active_menu->frame();
+ return frame.menubar_rect().contains(event.position().translated(-frame.rect().location()));
+ }();
+
+ // FIXME: This is quite hackish, we clear the hovered menu before potentially setting the same menu
+ // as hovered again. This makes sure that the hovered state doesn't linger after moving the
+ // cursor away from a hovered menu.
+ MenuManager::the().set_hovered_menu(nullptr);
+
+ if (MenuManager::the().has_open_menu()
+ || hitting_menu_in_window_with_active_menu
+ || menubar_rect().contains(event.position())) {
for_each_visible_window_of_type_from_front_to_back(WindowType::MenuApplet, [&](auto& window) {
if (!window.rect_in_menubar().contains(event.position()))
return IterationDecision::Continue;
@@ -923,8 +938,11 @@ void WindowManager::process_mouse_event(MouseEvent& event, Window*& hovered_wind
return IterationDecision::Break;
});
clear_resize_candidate();
- MenuManager::the().dispatch_event(event);
- return;
+
+ if (!hitting_menu_in_window_with_active_menu) {
+ MenuManager::the().dispatch_event(event);
+ return;
+ }
}
Window* event_window_with_frame = nullptr;
@@ -1456,8 +1474,8 @@ Gfx::IntRect WindowManager::maximized_window_rect(const Window& window) const
Gfx::IntRect rect = Screen::the().rect();
// Subtract window title bar (leaving the border)
- rect.set_y(rect.y() + window.frame().title_bar_rect().height());
- rect.set_height(rect.height() - window.frame().title_bar_rect().height());
+ rect.set_y(rect.y() + window.frame().title_bar_rect().height() + window.frame().menubar_rect().height());
+ rect.set_height(rect.height() - window.frame().title_bar_rect().height() - window.frame().menubar_rect().height());
// Subtract menu bar
rect.set_y(rect.y() + menubar_rect().height());
@@ -1611,4 +1629,15 @@ void WindowManager::reload_icon_bitmaps_after_scale_change(bool allow_hidpi_icon
return IterationDecision::Continue;
});
}
+
+void WindowManager::set_window_with_active_menu(Window* window)
+{
+ if (m_window_with_active_menu == window)
+ return;
+ if (window)
+ m_window_with_active_menu = window->make_weak_ptr<Window>();
+ else
+ m_window_with_active_menu = nullptr;
+}
+
}
diff --git a/Userland/Services/WindowServer/WindowManager.h b/Userland/Services/WindowServer/WindowManager.h
index 2056c8714c..96e5b63a4f 100644
--- a/Userland/Services/WindowServer/WindowManager.h
+++ b/Userland/Services/WindowServer/WindowManager.h
@@ -113,6 +113,10 @@ public:
const Window* active_input_window() const { return m_active_input_window.ptr(); }
const ClientConnection* active_client() const;
+ Window* window_with_active_menu() { return m_window_with_active_menu; }
+ const Window* window_with_active_menu() const { return m_window_with_active_menu; }
+ void set_window_with_active_menu(Window*);
+
const Window* highlight_window() const { return m_highlight_window.ptr(); }
void set_highlight_window(Window*);
@@ -329,6 +333,7 @@ private:
WeakPtr<Window> m_highlight_window;
WeakPtr<Window> m_active_input_window;
WeakPtr<Window> m_active_input_tracking_window;
+ WeakPtr<Window> m_window_with_active_menu;
WeakPtr<Window> m_move_window;
Gfx::IntPoint m_move_origin;
diff --git a/Userland/Services/WindowServer/WindowServer.ipc b/Userland/Services/WindowServer/WindowServer.ipc
index b908be52f6..c0dd477e0d 100644
--- a/Userland/Services/WindowServer/WindowServer.ipc
+++ b/Userland/Services/WindowServer/WindowServer.ipc
@@ -52,6 +52,8 @@ endpoint WindowServer = 2
DestroyWindow(i32 window_id) => (Vector<i32> destroyed_window_ids)
+ SetWindowMenubar(i32 window_id, i32 menubar_id) => ()
+
SetWindowTitle(i32 window_id, [UTF8] String title) => ()
GetWindowTitle(i32 window_id) => ([UTF8] String title)