mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 03:37:43 +00:00
LibGUI: TabWidget add vertical tabs
Add vertical tabs to TabWidget, this can be set using the ```TabWidget::set_tab_position``` function or in the GML
This commit is contained in:
parent
8a7876d65c
commit
5b82bd719e
7 changed files with 155 additions and 88 deletions
|
@ -34,4 +34,5 @@ Defines a GUI tab widget.
|
||||||
| show_close_buttons | bool | true or false | Show a close button on each tab |
|
| show_close_buttons | bool | true or false | Show a close button on each tab |
|
||||||
| show_tab_bar | bool | true or false | Whether to display the tabs |
|
| show_tab_bar | bool | true or false | Whether to display the tabs |
|
||||||
| text_alignment | text_alignment | Center, CenterLeft, CenterRight, TopLeft, TopRight, BottomLeft, BottomRight | Set the alignment of tab text |
|
| text_alignment | text_alignment | Center, CenterLeft, CenterRight, TopLeft, TopRight, BottomLeft, BottomRight | Set the alignment of tab text |
|
||||||
|
| tab_position | tab_position | Top, Bottom, Left, Right | Set the tab position |
|
||||||
| uniform_tabs | bool | true or false | Give all tabs the same width |
|
| uniform_tabs | bool | true or false | Give all tabs the same width |
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org>
|
* Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org>
|
||||||
|
* Copyright (c) 2022, Cameron Youell <cameronyouell@gmail.com>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -29,6 +30,11 @@ TabWidget::TabWidget()
|
||||||
REGISTER_BOOL_PROPERTY("show_tab_bar", is_bar_visible, set_bar_visible);
|
REGISTER_BOOL_PROPERTY("show_tab_bar", is_bar_visible, set_bar_visible);
|
||||||
REGISTER_BOOL_PROPERTY("reorder_allowed", reorder_allowed, set_reorder_allowed);
|
REGISTER_BOOL_PROPERTY("reorder_allowed", reorder_allowed, set_reorder_allowed);
|
||||||
REGISTER_BOOL_PROPERTY("uniform_tabs", uniform_tabs, set_uniform_tabs);
|
REGISTER_BOOL_PROPERTY("uniform_tabs", uniform_tabs, set_uniform_tabs);
|
||||||
|
REGISTER_ENUM_PROPERTY("tab_position", this->tab_position, set_tab_position, TabPosition,
|
||||||
|
{ TabPosition::Top, "Top" },
|
||||||
|
{ TabPosition::Bottom, "Bottom" },
|
||||||
|
{ TabPosition::Left, "Left" },
|
||||||
|
{ TabPosition::Right, "Right" }, );
|
||||||
|
|
||||||
register_property(
|
register_property(
|
||||||
"text_alignment",
|
"text_alignment",
|
||||||
|
@ -153,6 +159,12 @@ Gfx::IntRect TabWidget::child_rect_for_size(Gfx::IntSize const& size) const
|
||||||
case TabPosition::Bottom:
|
case TabPosition::Bottom:
|
||||||
rect = { { m_container_margins.left(), m_container_margins.top() }, { size.width() - m_container_margins.left() - m_container_margins.right(), size.height() - bar_height() - m_container_margins.top() - m_container_margins.bottom() } };
|
rect = { { m_container_margins.left(), m_container_margins.top() }, { size.width() - m_container_margins.left() - m_container_margins.right(), size.height() - bar_height() - m_container_margins.top() - m_container_margins.bottom() } };
|
||||||
break;
|
break;
|
||||||
|
case TabPosition::Left:
|
||||||
|
rect = { { get_max_tab_width() + m_container_margins.left(), m_container_margins.top() }, { size.width() - get_max_tab_width() - m_container_margins.left() - m_container_margins.right(), size.height() - m_container_margins.top() - m_container_margins.bottom() } };
|
||||||
|
break;
|
||||||
|
case TabPosition::Right:
|
||||||
|
rect = { { m_container_margins.left(), m_container_margins.top() }, { size.width() - get_max_tab_width() - m_container_margins.left() - m_container_margins.right(), size.height() - m_container_margins.top() - m_container_margins.bottom() } };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (rect.is_empty())
|
if (rect.is_empty())
|
||||||
return {};
|
return {};
|
||||||
|
@ -189,6 +201,10 @@ Gfx::IntRect TabWidget::bar_rect() const
|
||||||
return { 0, 0, width(), bar_height() };
|
return { 0, 0, width(), bar_height() };
|
||||||
case TabPosition::Bottom:
|
case TabPosition::Bottom:
|
||||||
return { 0, height() - bar_height(), width(), bar_height() };
|
return { 0, height() - bar_height(), width(), bar_height() };
|
||||||
|
case TabPosition::Left:
|
||||||
|
return { 0, 0, get_max_tab_width(), height() };
|
||||||
|
case TabPosition::Right:
|
||||||
|
return { width() - get_max_tab_width(), 0, get_max_tab_width(), height() };
|
||||||
}
|
}
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -200,6 +216,10 @@ Gfx::IntRect TabWidget::container_rect() const
|
||||||
return { 0, bar_height(), width(), height() - bar_height() };
|
return { 0, bar_height(), width(), height() - bar_height() };
|
||||||
case TabPosition::Bottom:
|
case TabPosition::Bottom:
|
||||||
return { 0, 0, width(), height() - bar_height() };
|
return { 0, 0, width(), height() - bar_height() };
|
||||||
|
case TabPosition::Left:
|
||||||
|
return { get_max_tab_width(), 0, width() - get_max_tab_width(), height() };
|
||||||
|
case TabPosition::Right:
|
||||||
|
return { 0, 0, width() - get_max_tab_width(), height() };
|
||||||
}
|
}
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -221,22 +241,11 @@ void TabWidget::paint_event(PaintEvent& event)
|
||||||
if (!icon)
|
if (!icon)
|
||||||
return;
|
return;
|
||||||
Gfx::IntRect icon_rect { button_rect.x(), button_rect.y(), 16, 16 };
|
Gfx::IntRect icon_rect { button_rect.x(), button_rect.y(), 16, 16 };
|
||||||
icon_rect.translate_by(4, 3);
|
icon_rect.translate_by(4, (button_rect.height() / 2) - (icon_rect.height() / 2));
|
||||||
|
|
||||||
painter.draw_scaled_bitmap(icon_rect, *icon, icon->rect());
|
painter.draw_scaled_bitmap(icon_rect, *icon, icon->rect());
|
||||||
text_rect.set_x(icon_rect.right() + 1 + 4);
|
text_rect.set_x(icon_rect.right() + 1 + 4);
|
||||||
text_rect.intersect(button_rect);
|
text_rect.intersect(button_rect);
|
||||||
|
|
||||||
// We want to be in perfect alignment with the icon rect at all times.
|
|
||||||
auto icon_rect_difference = icon_rect.top() - text_rect.top();
|
|
||||||
text_rect.set_top(text_rect.top() + icon_rect_difference);
|
|
||||||
text_rect.set_height(text_rect.height() - icon_rect_difference);
|
|
||||||
|
|
||||||
// ...unless our leftover height after text drawing is uneven, in which
|
|
||||||
// case we want to bias towards the bottom when the tab position is at
|
|
||||||
// the top.
|
|
||||||
if ((text_rect.height() - font().glyph_height()) % 2 != 0 && m_tab_position == TabPosition::Top) {
|
|
||||||
text_rect.set_top(text_rect.top() + 1);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t i = 0; i < m_tabs.size(); ++i) {
|
for (size_t i = 0; i < m_tabs.size(); ++i) {
|
||||||
|
@ -244,17 +253,9 @@ void TabWidget::paint_event(PaintEvent& event)
|
||||||
continue;
|
continue;
|
||||||
bool hovered = i == m_hovered_tab_index;
|
bool hovered = i == m_hovered_tab_index;
|
||||||
auto button_rect = this->button_rect(i);
|
auto button_rect = this->button_rect(i);
|
||||||
Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), false, hovered, m_tabs[i].widget->is_enabled(), m_tab_position == TabPosition::Top, window()->is_active());
|
Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), false, hovered, m_tabs[i].widget->is_enabled(), m_tab_position, window()->is_active());
|
||||||
|
|
||||||
// First we get rid of the 1px sheen on the left, then the 2px shadow
|
|
||||||
// on the right. Finally we shrink 3px from each side.
|
|
||||||
auto tab_button_content_rect = button_rect.shrunken(2, 0);
|
auto tab_button_content_rect = button_rect.shrunken(2, 0);
|
||||||
tab_button_content_rect.set_width(tab_button_content_rect.width() - 1);
|
|
||||||
tab_button_content_rect.shrink(6, 0);
|
|
||||||
if (m_tab_position == TabPosition::Top) {
|
|
||||||
tab_button_content_rect.set_top(tab_button_content_rect.top() + 1);
|
|
||||||
tab_button_content_rect.set_height(tab_button_content_rect.height() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect);
|
paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect);
|
||||||
tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0));
|
tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0));
|
||||||
|
@ -287,21 +288,16 @@ void TabWidget::paint_event(PaintEvent& event)
|
||||||
bool hovered = i == m_hovered_tab_index;
|
bool hovered = i == m_hovered_tab_index;
|
||||||
auto button_rect = this->button_rect(i);
|
auto button_rect = this->button_rect(i);
|
||||||
|
|
||||||
if (m_dragging_active_tab)
|
if (m_dragging_active_tab) {
|
||||||
button_rect.set_x(m_mouse_x - m_grab_offset);
|
if (this->has_vertical_tabs())
|
||||||
|
button_rect.set_y(m_mouse_pos - m_grab_offset);
|
||||||
Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), true, hovered, m_tabs[i].widget->is_enabled(), m_tab_position == TabPosition::Top, window()->is_active());
|
else
|
||||||
|
button_rect.set_x(m_mouse_pos - m_grab_offset);
|
||||||
// First we get rid of the 1px sheen on the left, then the 2px shadow
|
|
||||||
// on the right. Finally we shrink 3px from each side.
|
|
||||||
auto tab_button_content_rect = button_rect.shrunken(2, 0);
|
|
||||||
tab_button_content_rect.set_width(tab_button_content_rect.width() - 1);
|
|
||||||
tab_button_content_rect.shrink(6, 0);
|
|
||||||
if (m_tab_position == TabPosition::Top) {
|
|
||||||
tab_button_content_rect.set_top(tab_button_content_rect.top() + 1);
|
|
||||||
tab_button_content_rect.set_height(tab_button_content_rect.height() - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto tab_button_content_rect = button_rect.shrunken(2, 0);
|
||||||
|
Gfx::StylePainter::paint_tab_button(painter, button_rect, palette(), true, hovered, m_tabs[i].widget->is_enabled(), m_tab_position, window()->is_active());
|
||||||
|
|
||||||
paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect);
|
paint_tab_icon_if_needed(m_tabs[i].icon, button_rect, tab_button_content_rect);
|
||||||
tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0));
|
tab_button_content_rect.set_width(tab_button_content_rect.width() - (m_close_button_enabled ? 16 : 0));
|
||||||
|
|
||||||
|
@ -337,8 +333,12 @@ void TabWidget::paint_event(PaintEvent& event)
|
||||||
bool pressed_close_button = i == m_pressed_close_button_index;
|
bool pressed_close_button = i == m_pressed_close_button_index;
|
||||||
auto close_button_rect = this->close_button_rect(i);
|
auto close_button_rect = this->close_button_rect(i);
|
||||||
|
|
||||||
if (m_dragging_active_tab)
|
if (m_dragging_active_tab) {
|
||||||
close_button_rect.set_x((m_mouse_x - m_grab_offset) + (close_button_rect.x() - button_rect(i).x()));
|
if (this->has_vertical_tabs())
|
||||||
|
close_button_rect.set_y((m_mouse_pos - m_grab_offset) + (close_button_rect.y() - button_rect(i).y()));
|
||||||
|
else
|
||||||
|
close_button_rect.set_x((m_mouse_pos - m_grab_offset) + (close_button_rect.x() - button_rect(i).x()));
|
||||||
|
}
|
||||||
|
|
||||||
if (hovered_close_button)
|
if (hovered_close_button)
|
||||||
Gfx::StylePainter::paint_frame(painter, close_button_rect, palette(), Gfx::FrameShape::Box, pressed_close_button ? Gfx::FrameShadow::Sunken : Gfx::FrameShadow::Raised, 1);
|
Gfx::StylePainter::paint_frame(painter, close_button_rect, palette(), Gfx::FrameShape::Box, pressed_close_button ? Gfx::FrameShadow::Sunken : Gfx::FrameShadow::Raised, 1);
|
||||||
|
@ -351,14 +351,16 @@ void TabWidget::paint_event(PaintEvent& event)
|
||||||
|
|
||||||
int TabWidget::uniform_tab_width() const
|
int TabWidget::uniform_tab_width() const
|
||||||
{
|
{
|
||||||
int minimum_tab_width = 24;
|
int total_tab_width = m_tabs.size() * get_max_tab_width();
|
||||||
int maximum_tab_width = 160;
|
int tab_width = get_max_tab_width();
|
||||||
int total_tab_width = m_tabs.size() * maximum_tab_width;
|
|
||||||
int tab_width = maximum_tab_width;
|
if (this->has_vertical_tabs())
|
||||||
|
return tab_width;
|
||||||
|
|
||||||
int available_width = width() - bar_margin() * 2;
|
int available_width = width() - bar_margin() * 2;
|
||||||
if (total_tab_width > available_width)
|
if (total_tab_width > available_width)
|
||||||
tab_width = available_width / m_tabs.size();
|
tab_width = available_width / m_tabs.size();
|
||||||
return max(tab_width, minimum_tab_width);
|
return max(tab_width, m_min_tab_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabWidget::set_bar_visible(bool bar_visible)
|
void TabWidget::set_bar_visible(bool bar_visible)
|
||||||
|
@ -370,6 +372,30 @@ void TabWidget::set_bar_visible(bool bar_visible)
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::IntRect TabWidget::button_rect(size_t index) const
|
Gfx::IntRect TabWidget::button_rect(size_t index) const
|
||||||
|
{
|
||||||
|
if (this->has_vertical_tabs())
|
||||||
|
return vertical_button_rect(index);
|
||||||
|
return horizontal_button_rect(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::IntRect TabWidget::vertical_button_rect(size_t index) const
|
||||||
|
{
|
||||||
|
int offset = bar_margin() + (bar_height() * index);
|
||||||
|
Gfx::IntRect rect { 0, offset, get_max_tab_width() - 1, bar_height() };
|
||||||
|
|
||||||
|
if (m_tabs[index].widget != m_active_widget) {
|
||||||
|
rect.translate_by(m_tab_position == TabPosition::Left ? 2 : 0, 0);
|
||||||
|
rect.set_width(rect.width() - 2);
|
||||||
|
} else {
|
||||||
|
rect.translate_by(0, -2);
|
||||||
|
rect.set_height(rect.height() + 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.translate_by(bar_rect().location());
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::IntRect TabWidget::horizontal_button_rect(size_t index) const
|
||||||
{
|
{
|
||||||
int x_offset = bar_margin();
|
int x_offset = bar_margin();
|
||||||
int close_button_offset = m_close_button_enabled ? 16 : 0;
|
int close_button_offset = m_close_button_enabled ? 16 : 0;
|
||||||
|
@ -395,10 +421,8 @@ Gfx::IntRect TabWidget::close_button_rect(size_t index) const
|
||||||
auto rect = button_rect(index);
|
auto rect = button_rect(index);
|
||||||
Gfx::IntRect close_button_rect { 0, 0, 12, 12 };
|
Gfx::IntRect close_button_rect { 0, 0, 12, 12 };
|
||||||
|
|
||||||
if (m_tabs[index].widget == m_active_widget)
|
close_button_rect.translate_by(rect.right(), rect.top());
|
||||||
close_button_rect.translate_by(rect.right() - 16, rect.top() + (m_tab_position == TabPosition::Top ? 5 : 4));
|
close_button_rect.translate_by(-(close_button_rect.width() + 4), (rect.height() / 2) - (close_button_rect.height() / 2));
|
||||||
else
|
|
||||||
close_button_rect.translate_by(rect.right() - 15, rect.top() + (m_tab_position == TabPosition::Top ? 4 : 3));
|
|
||||||
|
|
||||||
return close_button_rect;
|
return close_button_rect;
|
||||||
}
|
}
|
||||||
|
@ -478,7 +502,7 @@ void TabWidget::mousemove_event(MouseEvent& event)
|
||||||
Optional<size_t> hovered_tab = {};
|
Optional<size_t> hovered_tab = {};
|
||||||
Optional<size_t> hovered_close_button = {};
|
Optional<size_t> hovered_close_button = {};
|
||||||
|
|
||||||
m_mouse_x = event.position().x();
|
m_mouse_pos = this->has_vertical_tabs() ? event.position().y() : event.position().x();
|
||||||
if (m_dragging_active_tab) {
|
if (m_dragging_active_tab) {
|
||||||
recalculate_tab_order();
|
recalculate_tab_order();
|
||||||
update_bar();
|
update_bar();
|
||||||
|
@ -528,6 +552,8 @@ void TabWidget::set_tab_position(TabPosition tab_position)
|
||||||
if (m_tab_position == tab_position)
|
if (m_tab_position == tab_position)
|
||||||
return;
|
return;
|
||||||
m_tab_position = tab_position;
|
m_tab_position = tab_position;
|
||||||
|
if (this->has_vertical_tabs())
|
||||||
|
m_uniform_tabs = true;
|
||||||
if (m_active_widget)
|
if (m_active_widget)
|
||||||
m_active_widget->set_relative_rect(child_rect_for_size(size()));
|
m_active_widget->set_relative_rect(child_rect_for_size(size()));
|
||||||
update();
|
update();
|
||||||
|
@ -667,7 +693,7 @@ void TabWidget::set_container_margins(GUI::Margins const& margins)
|
||||||
void TabWidget::drag_tab(size_t index)
|
void TabWidget::drag_tab(size_t index)
|
||||||
{
|
{
|
||||||
m_dragging_active_tab = m_reorder_allowed;
|
m_dragging_active_tab = m_reorder_allowed;
|
||||||
m_grab_offset = m_mouse_x - button_rect(index).x();
|
m_grab_offset = m_mouse_pos - (this->has_vertical_tabs() ? button_rect(index).y() : button_rect(index).x());
|
||||||
m_hovered_tab_index = {};
|
m_hovered_tab_index = {};
|
||||||
m_hovered_close_button_index = {};
|
m_hovered_close_button_index = {};
|
||||||
}
|
}
|
||||||
|
@ -685,16 +711,16 @@ void TabWidget::recalculate_tab_order()
|
||||||
|
|
||||||
size_t target;
|
size_t target;
|
||||||
for (target = 0; target < active; ++target) {
|
for (target = 0; target < active; ++target) {
|
||||||
auto button_rect = this->button_rect(target);
|
auto button_pos = this->has_vertical_tabs() ? (this->button_rect(target)).y() : (this->button_rect(target)).x();
|
||||||
if ((m_mouse_x - m_grab_offset) < button_rect.x()) {
|
if (m_mouse_pos - m_grab_offset < button_pos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target == active) {
|
if (target == active) {
|
||||||
for (target = m_tabs.size() - 1; target > active; --target) {
|
for (target = m_tabs.size() - 1; target > active; --target) {
|
||||||
auto button_rect = this->button_rect(target);
|
auto button_pos = this->has_vertical_tabs() ? (this->button_rect(target)).y() : (this->button_rect(target)).x();
|
||||||
if ((m_mouse_x - m_grab_offset) > button_rect.x()) {
|
if (m_mouse_pos - m_grab_offset > button_pos) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,12 +18,15 @@ public:
|
||||||
enum TabPosition {
|
enum TabPosition {
|
||||||
Top,
|
Top,
|
||||||
Bottom,
|
Bottom,
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~TabWidget() override = default;
|
virtual ~TabWidget() override = default;
|
||||||
|
|
||||||
TabPosition tab_position() const { return m_tab_position; }
|
TabPosition tab_position() const { return m_tab_position; }
|
||||||
void set_tab_position(TabPosition);
|
void set_tab_position(TabPosition);
|
||||||
|
bool has_vertical_tabs() const { return m_tab_position == TabPosition::Left || m_tab_position == TabPosition::Right; }
|
||||||
|
|
||||||
Optional<size_t> active_tab_index() const;
|
Optional<size_t> active_tab_index() const;
|
||||||
|
|
||||||
|
@ -32,7 +35,13 @@ public:
|
||||||
void set_active_widget(Widget*);
|
void set_active_widget(Widget*);
|
||||||
void set_tab_index(int);
|
void set_tab_index(int);
|
||||||
|
|
||||||
int bar_height() const { return m_bar_visible ? 21 : 0; }
|
int bar_height() const { return m_bar_visible ? 22 : 0; }
|
||||||
|
|
||||||
|
int get_max_tab_width() const { return m_bar_visible ? m_max_tab_width : 0; }
|
||||||
|
void set_max_tab_width(int width) { m_max_tab_width = width; }
|
||||||
|
|
||||||
|
int get_min_tab_width() const { return m_min_tab_width; }
|
||||||
|
void set_min_tab_width(int width) { m_min_tab_width = width; }
|
||||||
|
|
||||||
GUI::Margins const& container_margins() const { return m_container_margins; }
|
GUI::Margins const& container_margins() const { return m_container_margins; }
|
||||||
void set_container_margins(GUI::Margins const&);
|
void set_container_margins(GUI::Margins const&);
|
||||||
|
@ -110,6 +119,8 @@ protected:
|
||||||
private:
|
private:
|
||||||
Gfx::IntRect child_rect_for_size(Gfx::IntSize const&) const;
|
Gfx::IntRect child_rect_for_size(Gfx::IntSize const&) const;
|
||||||
Gfx::IntRect button_rect(size_t index) const;
|
Gfx::IntRect button_rect(size_t index) const;
|
||||||
|
Gfx::IntRect vertical_button_rect(size_t index) const;
|
||||||
|
Gfx::IntRect horizontal_button_rect(size_t index) const;
|
||||||
Gfx::IntRect close_button_rect(size_t index) const;
|
Gfx::IntRect close_button_rect(size_t index) const;
|
||||||
Gfx::IntRect bar_rect() const;
|
Gfx::IntRect bar_rect() const;
|
||||||
Gfx::IntRect container_rect() const;
|
Gfx::IntRect container_rect() const;
|
||||||
|
@ -136,10 +147,13 @@ private:
|
||||||
bool m_bar_visible { true };
|
bool m_bar_visible { true };
|
||||||
bool m_close_button_enabled { false };
|
bool m_close_button_enabled { false };
|
||||||
|
|
||||||
|
int m_max_tab_width { 160 };
|
||||||
|
int m_min_tab_width { 24 };
|
||||||
|
|
||||||
bool m_reorder_allowed { false };
|
bool m_reorder_allowed { false };
|
||||||
bool m_dragging_active_tab { false };
|
bool m_dragging_active_tab { false };
|
||||||
int m_grab_offset { 0 };
|
int m_grab_offset { 0 };
|
||||||
int m_mouse_x { 0 };
|
int m_mouse_pos { 0 };
|
||||||
|
|
||||||
void drag_tab(size_t index);
|
void drag_tab(size_t index);
|
||||||
void recalculate_tab_order();
|
void recalculate_tab_order();
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
||||||
* Copyright (c) 2020, Sarah Taube <metalflakecobaltpaint@gmail.com>
|
* Copyright (c) 2020, Sarah Taube <metalflakecobaltpaint@gmail.com>
|
||||||
* Copyright (c) 2021, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
|
* Copyright (c) 2021, Filiph Sandström <filiph.sandstrom@filfatstudios.com>
|
||||||
|
* Copyright (c) 2022, Cameron Youell <cameronyouell@gmail.com>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
@ -17,7 +18,7 @@
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
void ClassicStylePainter::paint_tab_button(Painter& painter, IntRect const& rect, Palette const& palette, bool active, bool hovered, bool enabled, bool top, bool in_active_window)
|
void ClassicStylePainter::paint_tab_button(Painter& painter, IntRect const& rect, Palette const& palette, bool active, bool hovered, bool enabled, GUI::TabWidget::TabPosition position, bool in_active_window)
|
||||||
{
|
{
|
||||||
Color base_color = palette.button();
|
Color base_color = palette.button();
|
||||||
Color highlight_color2 = palette.threed_highlight();
|
Color highlight_color2 = palette.threed_highlight();
|
||||||
|
@ -30,18 +31,19 @@ void ClassicStylePainter::paint_tab_button(Painter& painter, IntRect const& rect
|
||||||
PainterStateSaver saver(painter);
|
PainterStateSaver saver(painter);
|
||||||
painter.translate(rect.location());
|
painter.translate(rect.location());
|
||||||
|
|
||||||
if (top) {
|
auto accent = palette.accent();
|
||||||
|
if (!in_active_window)
|
||||||
|
accent = accent.to_grayscale();
|
||||||
|
|
||||||
|
switch (position) {
|
||||||
|
case GUI::TabWidget::TabPosition::Top:
|
||||||
// Base
|
// Base
|
||||||
painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 1 }, base_color);
|
painter.fill_rect({ 1, 1, rect.width() - 2, rect.height() - 1 }, base_color);
|
||||||
|
|
||||||
// Top line
|
// Top line
|
||||||
if (active) {
|
if (active) {
|
||||||
auto accent = palette.accent();
|
|
||||||
if (!in_active_window)
|
|
||||||
accent = accent.to_grayscale();
|
|
||||||
painter.draw_line({ 3, 0 }, { rect.width() - 3, 0 }, accent.darkened());
|
painter.draw_line({ 3, 0 }, { rect.width() - 3, 0 }, accent.darkened());
|
||||||
Gfx::IntRect accent_rect { 1, 1, rect.width() - 2, 2 };
|
painter.fill_rect_with_gradient({ 1, 1, rect.width() - 2, 2 }, accent, accent.lightened(1.5f));
|
||||||
painter.fill_rect_with_gradient(accent_rect, accent, accent.lightened(1.5f));
|
|
||||||
painter.set_pixel({ 2, 0 }, highlight_color2);
|
painter.set_pixel({ 2, 0 }, highlight_color2);
|
||||||
} else {
|
} else {
|
||||||
painter.draw_line({ 2, 0 }, { rect.width() - 3, 0 }, highlight_color2);
|
painter.draw_line({ 2, 0 }, { rect.width() - 3, 0 }, highlight_color2);
|
||||||
|
@ -52,27 +54,17 @@ void ClassicStylePainter::paint_tab_button(Painter& painter, IntRect const& rect
|
||||||
painter.set_pixel({ 1, 1 }, highlight_color2);
|
painter.set_pixel({ 1, 1 }, highlight_color2);
|
||||||
|
|
||||||
// Right side
|
// Right side
|
||||||
|
painter.draw_line({ rect.width() - 1, 2 }, { rect.width() - 1, rect.height() - 1 }, shadow_color2);
|
||||||
IntPoint top_right_outer { rect.width() - 1, 2 };
|
painter.draw_line({ rect.width() - 2, 2 }, { rect.width() - 2, rect.height() - 1 }, shadow_color1);
|
||||||
IntPoint bottom_right_outer { rect.width() - 1, rect.height() - 1 };
|
|
||||||
painter.draw_line(top_right_outer, bottom_right_outer, shadow_color2);
|
|
||||||
|
|
||||||
IntPoint top_right_inner { rect.width() - 2, 2 };
|
|
||||||
IntPoint bottom_right_inner { rect.width() - 2, rect.height() - 1 };
|
|
||||||
painter.draw_line(top_right_inner, bottom_right_inner, shadow_color1);
|
|
||||||
|
|
||||||
painter.set_pixel(rect.width() - 2, 1, shadow_color2);
|
painter.set_pixel(rect.width() - 2, 1, shadow_color2);
|
||||||
} else {
|
break;
|
||||||
|
case GUI::TabWidget::TabPosition::Bottom:
|
||||||
// Base
|
// Base
|
||||||
painter.fill_rect({ 0, 0, rect.width() - 1, rect.height() }, base_color);
|
painter.fill_rect({ 0, 0, rect.width() - 1, rect.height() }, base_color);
|
||||||
|
|
||||||
// Bottom line
|
// Bottom line
|
||||||
if (active) {
|
if (active) {
|
||||||
auto accent = palette.accent();
|
painter.fill_rect_with_gradient({ 1, rect.height() - 3, rect.width() - 2, 2 }, accent, accent.lightened(1.5f));
|
||||||
if (!in_active_window)
|
|
||||||
accent = accent.to_grayscale();
|
|
||||||
Gfx::IntRect accent_rect { 1, rect.height() - 3, rect.width() - 2, 2 };
|
|
||||||
painter.fill_rect_with_gradient(accent_rect, accent, accent.lightened(1.5f));
|
|
||||||
painter.draw_line({ 2, rect.height() - 1 }, { rect.width() - 3, rect.height() - 1 }, accent.darkened());
|
painter.draw_line({ 2, rect.height() - 1 }, { rect.width() - 3, rect.height() - 1 }, accent.darkened());
|
||||||
} else {
|
} else {
|
||||||
painter.draw_line({ 2, rect.height() - 1 }, { rect.width() - 3, rect.height() - 1 }, shadow_color2);
|
painter.draw_line({ 2, rect.height() - 1 }, { rect.width() - 3, rect.height() - 1 }, shadow_color2);
|
||||||
|
@ -83,15 +75,48 @@ void ClassicStylePainter::paint_tab_button(Painter& painter, IntRect const& rect
|
||||||
painter.set_pixel({ 1, rect.height() - 2 }, highlight_color2);
|
painter.set_pixel({ 1, rect.height() - 2 }, highlight_color2);
|
||||||
|
|
||||||
// Right side
|
// Right side
|
||||||
IntPoint top_right_outer { rect.width() - 1, 0 };
|
painter.draw_line({ rect.width() - 1, 0 }, { rect.width() - 1, rect.height() - 3 }, shadow_color2);
|
||||||
IntPoint bottom_right_outer { rect.width() - 1, rect.height() - 3 };
|
painter.draw_line({ rect.width() - 2, 0 }, { rect.width() - 2, rect.height() - 3 }, shadow_color1);
|
||||||
painter.draw_line(top_right_outer, bottom_right_outer, shadow_color2);
|
painter.set_pixel({ rect.width() - 2, rect.height() - 2 }, shadow_color2);
|
||||||
|
break;
|
||||||
|
case GUI::TabWidget::TabPosition::Left:
|
||||||
|
// Base tab
|
||||||
|
painter.fill_rect({ 1, 1, rect.width(), rect.height() - 1 }, base_color);
|
||||||
|
painter.draw_line({ 2, 0 }, { rect.width(), 0 }, highlight_color2);
|
||||||
|
painter.draw_line({ 2, rect.height() - 1 }, { rect.width(), rect.height() - 1 }, shadow_color2);
|
||||||
|
|
||||||
IntPoint top_right_inner { rect.width() - 2, 0 };
|
// If the tab is active, draw the accent line
|
||||||
IntPoint bottom_right_inner { rect.width() - 2, rect.height() - 3 };
|
if (active) {
|
||||||
painter.draw_line(top_right_inner, bottom_right_inner, shadow_color1);
|
painter.fill_rect_with_gradient({ 1, 1, 2, rect.height() - 2 }, accent, accent.lightened(1.5f));
|
||||||
|
painter.draw_line({ 0, 2 }, { 0, rect.height() - 3 }, accent.darkened());
|
||||||
|
} else {
|
||||||
|
painter.draw_line({ 0, 2 }, { 0, rect.height() - 3 }, highlight_color2);
|
||||||
|
painter.draw_line({ rect.width(), 1 }, { rect.width(), rect.height() - 1 }, shadow_color1);
|
||||||
|
}
|
||||||
|
|
||||||
painter.set_pixel(rect.width() - 2, rect.height() - 2, shadow_color2);
|
// Make appear as if the tab is rounded
|
||||||
|
painter.set_pixel({ 1, 1 }, highlight_color2);
|
||||||
|
painter.set_pixel({ 1, rect.height() - 2 }, shadow_color2);
|
||||||
|
break;
|
||||||
|
case GUI::TabWidget::TabPosition::Right:
|
||||||
|
// Base tab
|
||||||
|
painter.fill_rect({ 0, 1, rect.width() - 1, rect.height() - 1 }, base_color);
|
||||||
|
painter.draw_line({ 0, 0 }, { rect.width() - 2, 0 }, highlight_color2);
|
||||||
|
painter.draw_line({ 0, rect.height() - 1 }, { rect.width() - 2, rect.height() - 1 }, shadow_color2);
|
||||||
|
|
||||||
|
// If the tab is active, draw the accent line
|
||||||
|
if (active) {
|
||||||
|
painter.fill_rect_with_gradient({ rect.width() - 2, 1, 2, rect.height() - 2 }, accent.lightened(1.5f), accent);
|
||||||
|
painter.draw_line({ rect.width(), 2 }, { rect.width(), rect.height() - 3 }, accent.darkened());
|
||||||
|
} else {
|
||||||
|
painter.draw_line({ rect.width(), 2 }, { rect.width(), rect.height() - 3 }, shadow_color2);
|
||||||
|
painter.draw_line({ 0, 0 }, { 0, rect.height() - 1 }, shadow_color1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make appear as if the tab is rounded
|
||||||
|
painter.set_pixel({ rect.width() - 1, 1 }, shadow_color1);
|
||||||
|
painter.set_pixel({ rect.width() - 1, rect.height() - 2 }, shadow_color2);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ namespace Gfx {
|
||||||
class ClassicStylePainter : public BaseStylePainter {
|
class ClassicStylePainter : public BaseStylePainter {
|
||||||
public:
|
public:
|
||||||
virtual void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false) override;
|
virtual void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false) override;
|
||||||
virtual void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, bool top, bool in_active_window) override;
|
virtual void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, GUI::TabWidget::TabPosition position, bool in_active_window) override;
|
||||||
virtual void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false) override;
|
virtual void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false) override;
|
||||||
virtual void paint_window_frame(Painter&, IntRect const&, Palette const&) override;
|
virtual void paint_window_frame(Painter&, IntRect const&, Palette const&) override;
|
||||||
virtual void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal) override;
|
virtual void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal) override;
|
||||||
|
|
|
@ -18,9 +18,9 @@ BaseStylePainter& StylePainter::current()
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StylePainter::paint_tab_button(Painter& painter, IntRect const& rect, Palette const& palette, bool active, bool hovered, bool enabled, bool top, bool in_active_window)
|
void StylePainter::paint_tab_button(Painter& painter, IntRect const& rect, Palette const& palette, bool active, bool hovered, bool enabled, GUI::TabWidget::TabPosition position, bool in_active_window)
|
||||||
{
|
{
|
||||||
current().paint_tab_button(painter, rect, palette, active, hovered, enabled, top, in_active_window);
|
current().paint_tab_button(painter, rect, palette, active, hovered, enabled, position, in_active_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StylePainter::paint_button(Painter& painter, IntRect const& rect, Palette const& palette, ButtonStyle button_style, bool pressed, bool hovered, bool checked, bool enabled, bool focused, bool default_button)
|
void StylePainter::paint_button(Painter& painter, IntRect const& rect, Palette const& palette, ButtonStyle button_style, bool pressed, bool hovered, bool checked, bool enabled, bool focused, bool default_button)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/Forward.h>
|
#include <AK/Forward.h>
|
||||||
|
#include <LibGUI/TabWidget.h>
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
#include <LibGfx/Orientation.h>
|
#include <LibGfx/Orientation.h>
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ public:
|
||||||
virtual ~BaseStylePainter() = default;
|
virtual ~BaseStylePainter() = default;
|
||||||
|
|
||||||
virtual void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false) = 0;
|
virtual void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false) = 0;
|
||||||
virtual void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, bool top, bool in_active_window) = 0;
|
virtual void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, GUI::TabWidget::TabPosition position, bool in_active_window) = 0;
|
||||||
virtual void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false) = 0;
|
virtual void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false) = 0;
|
||||||
virtual void paint_window_frame(Painter&, IntRect const&, Palette const&) = 0;
|
virtual void paint_window_frame(Painter&, IntRect const&, Palette const&) = 0;
|
||||||
virtual void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal) = 0;
|
virtual void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal) = 0;
|
||||||
|
@ -56,7 +57,7 @@ public:
|
||||||
|
|
||||||
// FIXME: These are here for API compatibility, we should probably remove them and move BaseStylePainter into here
|
// FIXME: These are here for API compatibility, we should probably remove them and move BaseStylePainter into here
|
||||||
static void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false);
|
static void paint_button(Painter&, IntRect const&, Palette const&, ButtonStyle, bool pressed, bool hovered = false, bool checked = false, bool enabled = true, bool focused = false, bool default_button = false);
|
||||||
static void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, bool top, bool in_active_window);
|
static void paint_tab_button(Painter&, IntRect const&, Palette const&, bool active, bool hovered, bool enabled, GUI::TabWidget::TabPosition position, bool in_active_window);
|
||||||
static void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false);
|
static void paint_frame(Painter&, IntRect const&, Palette const&, FrameShape, FrameShadow, int thickness, bool skip_vertical_lines = false);
|
||||||
static void paint_window_frame(Painter&, IntRect const&, Palette const&);
|
static void paint_window_frame(Painter&, IntRect const&, Palette const&);
|
||||||
static void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal);
|
static void paint_progressbar(Painter&, IntRect const&, Palette const&, int min, int max, int value, StringView text, Orientation = Orientation::Horizontal);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue