From 44c0672d59a97464f0d52dfae466ce0230fe5ba9 Mon Sep 17 00:00:00 2001 From: MacDue Date: Mon, 2 May 2022 21:34:33 +0100 Subject: [PATCH] WindowServer: Support hover icons for title buttons This allows adding "-hover.png" variants of the title button icons. This can be useful for themes which use the TitleButtonsIconOnly flag, which otherwise don't have a way of showing the hover state. --- Userland/Services/WindowServer/Button.cpp | 11 +++-- Userland/Services/WindowServer/Button.h | 9 +++- .../Services/WindowServer/WindowFrame.cpp | 45 ++++++++++++------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/Userland/Services/WindowServer/Button.cpp b/Userland/Services/WindowServer/Button.cpp index 2e578738c6..d6d9769ec5 100644 --- a/Userland/Services/WindowServer/Button.cpp +++ b/Userland/Services/WindowServer/Button.cpp @@ -31,13 +31,18 @@ void Button::paint(Screen& screen, Gfx::Painter& painter) if (m_style == Style::Normal) Gfx::StylePainter::paint_button(painter, rect(), palette, Gfx::ButtonStyle::Normal, m_pressed, m_hovered); - if (m_icon) { - auto& bitmap = m_icon->bitmap(screen.scale_factor()); + auto paint_icon = [&](auto& multiscale_bitmap) { + auto& bitmap = multiscale_bitmap->bitmap(screen.scale_factor()); auto icon_location = rect().center().translated(-(bitmap.width() / 2), -(bitmap.height() / 2)); if (m_pressed) painter.translate(1, 1); painter.blit(icon_location, bitmap, bitmap.rect()); - } + }; + + if (m_icon.hover_bitmap && m_hovered) + paint_icon(m_icon.hover_bitmap); + else if (m_icon.bitmap) + paint_icon(m_icon.bitmap); } void Button::on_mouse_event(MouseEvent const& event) diff --git a/Userland/Services/WindowServer/Button.h b/Userland/Services/WindowServer/Button.h index bd65713261..d6643c2193 100644 --- a/Userland/Services/WindowServer/Button.h +++ b/Userland/Services/WindowServer/Button.h @@ -40,7 +40,12 @@ public: bool is_visible() const { return m_visible; } - void set_icon(RefPtr const& icon) { m_icon = icon; } + struct Icon { + RefPtr bitmap { nullptr }; + RefPtr hover_bitmap { nullptr }; + }; + + void set_icon(Icon const& icon) { m_icon = icon; } enum class Style { Normal, @@ -52,7 +57,7 @@ public: private: WindowFrame& m_frame; Gfx::IntRect m_relative_rect; - RefPtr m_icon; + Icon m_icon; bool m_pressed { false }; bool m_visible { true }; bool m_hovered { false }; diff --git a/Userland/Services/WindowServer/WindowFrame.cpp b/Userland/Services/WindowServer/WindowFrame.cpp index 6daa53b1fb..6d241b40c4 100644 --- a/Userland/Services/WindowServer/WindowFrame.cpp +++ b/Userland/Services/WindowServer/WindowFrame.cpp @@ -35,11 +35,11 @@ static Gfx::WindowTheme::WindowType to_theme_window_type(WindowType type) } } -static RefPtr s_minimize_icon; -static RefPtr s_maximize_icon; -static RefPtr s_restore_icon; -static RefPtr s_close_icon; -static RefPtr s_close_modified_icon; +static Button::Icon s_minimize_icon; +static Button::Icon s_maximize_icon; +static Button::Icon s_restore_icon; +static Button::Icon s_close_icon; +static Button::Icon s_close_modified_icon; static RefPtr s_active_window_shadow; static RefPtr s_inactive_window_shadow; @@ -125,7 +125,7 @@ void WindowFrame::set_button_icons() : Button::Style::Normal; if (m_window.is_closeable()) { - m_close_button->set_icon(m_window.is_modified() ? *s_close_modified_icon : *s_close_icon); + m_close_button->set_icon(m_window.is_modified() ? s_close_modified_icon : s_close_icon); m_close_button->set_style(button_style); } if (m_window.is_minimizable()) { @@ -133,7 +133,7 @@ void WindowFrame::set_button_icons() m_minimize_button->set_style(button_style); } if (m_window.is_resizable()) { - m_maximize_button->set_icon(m_window.is_maximized() ? *s_restore_icon : *s_maximize_icon); + m_maximize_button->set_icon(m_window.is_maximized() ? s_restore_icon : s_maximize_icon); m_maximize_button->set_style(button_style); } } @@ -142,21 +142,32 @@ void WindowFrame::reload_config() { String icons_path = WindowManager::the().palette().title_button_icons_path(); - auto reload_icon = [&](RefPtr& icon, StringView path, StringView default_path) { + auto reload_bitmap = [&](RefPtr& multiscale_bitmap, StringView path, StringView default_path = "") { StringBuilder full_path; full_path.append(icons_path); full_path.append(path); - if (icon) - icon->load(full_path.to_string(), default_path); + if (multiscale_bitmap) + multiscale_bitmap->load(full_path.string_view(), default_path); else - icon = MultiScaleBitmaps::create(full_path.to_string(), default_path); + multiscale_bitmap = MultiScaleBitmaps::create(full_path.string_view(), default_path); }; - reload_icon(s_minimize_icon, "window-minimize.png", "/res/icons/16x16/downward-triangle.png"); - reload_icon(s_maximize_icon, "window-maximize.png", "/res/icons/16x16/upward-triangle.png"); - reload_icon(s_restore_icon, "window-restore.png", "/res/icons/16x16/window-restore.png"); - reload_icon(s_close_icon, "window-close.png", "/res/icons/16x16/window-close.png"); - reload_icon(s_close_modified_icon, "window-close-modified.png", "/res/icons/16x16/window-close-modified.png"); + auto reload_icon = [&](Button::Icon& icon, StringView name, StringView default_path) { + StringBuilder full_name; + full_name.append(name); + full_name.append(".png"); + reload_bitmap(icon.bitmap, full_name.string_view(), default_path); + // Note: No default for hover bitmaps + full_name.clear(); + full_name.append(name); + full_name.append("-hover.png"); + reload_bitmap(icon.hover_bitmap, full_name.string_view()); + }; + reload_icon(s_minimize_icon, "window-minimize", "/res/icons/16x16/downward-triangle.png"); + reload_icon(s_maximize_icon, "window-maximize", "/res/icons/16x16/upward-triangle.png"); + reload_icon(s_restore_icon, "window-restore", "/res/icons/16x16/window-restore.png"); + reload_icon(s_close_icon, "window-close", "/res/icons/16x16/window-close.png"); + reload_icon(s_close_modified_icon, "window-close-modified", "/res/icons/16x16/window-close-modified.png"); auto load_shadow = [](String const& path, String& last_path, RefPtr& shadow_bitmap) { if (path.is_empty()) { @@ -217,7 +228,7 @@ bool WindowFrame::has_shadow() const void WindowFrame::did_set_maximized(Badge, bool maximized) { VERIFY(m_maximize_button); - m_maximize_button->set_icon(maximized ? *s_restore_icon : *s_maximize_icon); + m_maximize_button->set_icon(maximized ? s_restore_icon : s_maximize_icon); } Gfx::IntRect WindowFrame::menubar_rect() const