From 099f18faffd4ce9bfa75366fc1737036f8e6c81e Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Thu, 28 Mar 2024 12:17:07 -0400 Subject: [PATCH] LibGUI: Add initial support for an action icon in the tab widget bar An action icon will be a button-like component to the left of the tab icon. This initial patch just adds support for rendering an icon. In the future, we will want to accept an action to be triggered when the action icon is clicked, and to paint the icon like a button. --- Userland/Libraries/LibGUI/TabWidget.cpp | 38 +++++++++++++++++++++---- Userland/Libraries/LibGUI/TabWidget.h | 2 ++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibGUI/TabWidget.cpp b/Userland/Libraries/LibGUI/TabWidget.cpp index 6d98279cc21..6222342c3c5 100644 --- a/Userland/Libraries/LibGUI/TabWidget.cpp +++ b/Userland/Libraries/LibGUI/TabWidget.cpp @@ -42,7 +42,7 @@ TabWidget::TabWidget() ErrorOr TabWidget::try_add_widget(Widget& widget) { - TRY(m_tabs.try_append({ widget.title(), nullptr, &widget, false })); + TRY(m_tabs.try_append({ widget.title(), nullptr, nullptr, &widget, false })); TRY(try_add_child(widget)); update_focus_policy(); if (on_tab_count_change) @@ -235,15 +235,23 @@ void TabWidget::paint_event(PaintEvent& event) Gfx::StylePainter::paint_frame(painter, container_rect(), palette(), Gfx::FrameStyle::RaisedContainer); } - auto paint_tab_icon_if_needed = [&](auto& icon, auto& button_rect, auto& text_rect) { - if (!icon) - return; + auto make_icon_rect = [](auto const& button_rect) { Gfx::IntRect icon_rect { button_rect.x(), button_rect.y(), 16, 16 }; icon_rect.translate_by(4, (button_rect.height() / 2) - (icon_rect.height() / 2)); + return icon_rect; + }; + + auto paint_tab_icon_if_needed = [&](auto& icon, auto& button_rect, auto& icon_rect, auto& text_rect) { + if (!icon) + return; + painter.draw_scaled_bitmap(icon_rect, *icon, icon->rect()); + text_rect.set_x(icon_rect.right() + 4); text_rect.intersect(button_rect); + + icon_rect.set_x(text_rect.x()); }; bool accented = Desktop::the().system_effects().tab_accents(); @@ -256,8 +264,10 @@ void TabWidget::paint_event(PaintEvent& event) Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), false, hovered, m_tabs[i].widget->is_enabled(), m_tab_position, window()->is_active(), accented); auto tab_button_content_rect = button_rect.shrunken(8, 0); + auto icon_rect = make_icon_rect(button_rect); - paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect); + paint_tab_icon_if_needed(m_tabs[i].action_icon, button_rect, icon_rect, tab_button_content_rect); + paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, icon_rect, tab_button_content_rect); tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0)); painter.draw_text(tab_button_content_rect, m_tabs[i].title, m_text_alignment, palette().button_text(), Gfx::TextElision::Right); @@ -302,9 +312,12 @@ void TabWidget::paint_event(PaintEvent& event) } auto tab_button_content_rect = button_rect.shrunken(8, 0); + auto icon_rect = make_icon_rect(button_rect); + Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), true, hovered, m_tabs[i].widget->is_enabled(), m_tab_position, window()->is_active(), accented); - paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect); + paint_tab_icon_if_needed(m_tabs[i].action_icon, button_rect, icon_rect, tab_button_content_rect); + paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, icon_rect, tab_button_content_rect); tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0)); painter.draw_text(tab_button_content_rect, m_tabs[i].title, m_text_alignment, palette().button_text(), Gfx::TextElision::Right); @@ -603,6 +616,19 @@ void TabWidget::set_tab_icon(Widget& tab, Gfx::Bitmap const* icon) } } +// FIXME: Also accept an action to be triggered when the action icon is clicked. If the action is non-null, then also +// paint the icon as a button (with hover/click effects). +void TabWidget::set_tab_action_icon(Widget& tab, Gfx::Bitmap const* action_icon) +{ + for (auto& t : m_tabs) { + if (t.widget == &tab) { + t.action_icon = action_icon; + update(); + return; + } + } +} + bool TabWidget::is_tab_modified(Widget& tab_input) { auto it = m_tabs.find_if([&](auto t) { return t.widget == &tab_input; }); diff --git a/Userland/Libraries/LibGUI/TabWidget.h b/Userland/Libraries/LibGUI/TabWidget.h index f69b1914a11..e864a5193c5 100644 --- a/Userland/Libraries/LibGUI/TabWidget.h +++ b/Userland/Libraries/LibGUI/TabWidget.h @@ -69,6 +69,7 @@ public: void set_tab_title(Widget& tab, String title); void set_tab_icon(Widget& tab, Gfx::Bitmap const*); + void set_tab_action_icon(Widget& tab, Gfx::Bitmap const*); bool is_tab_modified(Widget& tab); void set_tab_modified(Widget& tab, bool modified); @@ -132,6 +133,7 @@ private: struct TabData { int width(Gfx::Font const&) const; String title; + RefPtr action_icon; RefPtr icon; Widget* widget { nullptr }; bool modified { false };