diff options
Diffstat (limited to 'Userland/Services/WindowServer')
-rw-r--r-- | Userland/Services/WindowServer/ClientConnection.cpp | 26 | ||||
-rw-r--r-- | Userland/Services/WindowServer/ClientConnection.h | 12 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Menu.cpp | 2 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Menu.h | 18 | ||||
-rw-r--r-- | Userland/Services/WindowServer/MenuBar.h | 8 | ||||
-rw-r--r-- | Userland/Services/WindowServer/MenuManager.cpp | 43 | ||||
-rw-r--r-- | Userland/Services/WindowServer/MenuManager.h | 6 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.cpp | 32 | ||||
-rw-r--r-- | Userland/Services/WindowServer/Window.h | 8 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowFrame.cpp | 110 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowFrame.h | 11 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowManager.cpp | 41 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowManager.h | 5 | ||||
-rw-r--r-- | Userland/Services/WindowServer/WindowServer.ipc | 2 |
14 files changed, 294 insertions, 30 deletions
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) |