Skip to content

Commit

Permalink
Allow adding buttons to the TabContainer header
Browse files Browse the repository at this point in the history
  • Loading branch information
raulsntos committed Sep 24, 2023
1 parent c12d635 commit 5c4a414
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 10 deletions.
17 changes: 17 additions & 0 deletions doc/classes/TabContainer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@
<link title="Using Containers">$DOCS_URL/tutorials/ui/gui_containers.html</link>
</tutorials>
<methods>
<method name="add_button">
<return type="void" />
<param index="0" name="button" type="Control" />
<description>
Add a [Button] to the tabs header.
</description>
</method>
<method name="get_current_tab_control" qualifiers="const">
<return type="Control" />
<description>
Expand Down Expand Up @@ -99,6 +106,13 @@
Returns [code]true[/code] if the tab at index [param tab_idx] is hidden.
</description>
</method>
<method name="remove_button">
<return type="void" />
<param index="0" name="button" type="Control" />
<description>
Remove a [Button] from the tabs header.
</description>
</method>
<method name="set_popup">
<return type="void" />
<param index="0" name="popup" type="Node" />
Expand Down Expand Up @@ -244,6 +258,9 @@
<theme_item name="font_unselected_color" data_type="color" type="Color" default="Color(0.7, 0.7, 0.7, 1)">
Font color of the other, unselected tabs.
</theme_item>
<theme_item name="buttons_separation" data_type="constant" type="int" default="4">
The size of the space between each of the buttons in the [TabBar].
</theme_item>
<theme_item name="icon_max_width" data_type="constant" type="int" default="0">
The maximum allowed width of the tab's icon. This limit is applied on top of the default size of the icon, but before the value set with [method TabBar.set_tab_icon_max_width]. The height is adjusted according to the icon's ratio.
</theme_item>
Expand Down
87 changes: 77 additions & 10 deletions scene/gui/tab_container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "tab_container.h"

#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
#include "scene/theme/theme_db.h"
Expand Down Expand Up @@ -175,6 +176,17 @@ void TabContainer::_notification(int p_what) {
theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_icon->get_height()) / 2));
}
}

if (buttons_container->get_child_count() > 0) {
buttons_container->reset_size();
int x = is_layout_rtl() ? 0 : get_size().width - buttons_container->get_size().x;
int y = (header_height - buttons_container->get_size().y) / 2;
if (get_popup()) {
int menu_width = theme_cache.menu_icon->get_width() + theme_cache.buttons_separation;
x += is_layout_rtl() ? menu_width : -menu_width;
}
buttons_container->set_position(Point2(x, y));
}
} break;

case NOTIFICATION_TRANSLATION_CHANGED:
Expand Down Expand Up @@ -216,6 +228,8 @@ void TabContainer::_on_theme_changed() {
tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width);
tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size);

buttons_container->add_theme_constant_override(SNAME("separation"), theme_cache.buttons_separation);

_update_margins();
if (get_tab_count() > 0) {
_repaint();
Expand Down Expand Up @@ -259,30 +273,40 @@ void TabContainer::_update_margins() {

// Directly check for validity, to avoid errors when quitting.
bool has_popup = popup_obj_id.is_valid();
bool has_buttons = buttons_container->get_child_count() > 0;

int buttons_offset = has_popup ? menu_width : 0;
if (has_buttons) {
buttons_container->reset_size();
buttons_offset += buttons_container->get_size().x + theme_cache.buttons_separation;
if (has_popup) {
buttons_offset += theme_cache.buttons_separation;
}
}

if (get_tab_count() == 0) {
tab_bar->set_offset(SIDE_LEFT, 0);
tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
tab_bar->set_offset(SIDE_RIGHT, -buttons_offset);

return;
}

switch (get_tab_alignment()) {
case TabBar::ALIGNMENT_LEFT: {
tab_bar->set_offset(SIDE_LEFT, theme_cache.side_margin);
tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
tab_bar->set_offset(SIDE_RIGHT, -buttons_offset);
} break;

case TabBar::ALIGNMENT_CENTER: {
tab_bar->set_offset(SIDE_LEFT, 0);
tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
tab_bar->set_offset(SIDE_RIGHT, -buttons_offset);
} break;

case TabBar::ALIGNMENT_RIGHT: {
tab_bar->set_offset(SIDE_LEFT, 0);

if (has_popup) {
tab_bar->set_offset(SIDE_RIGHT, -menu_width);
if (has_popup || has_buttons) {
tab_bar->set_offset(SIDE_RIGHT, -buttons_offset);
return;
}

Expand All @@ -292,7 +316,7 @@ void TabContainer::_update_margins() {

// Calculate if all the tabs would still fit if the margin was present.
if (get_clip_tabs() && (tab_bar->get_offset_buttons_visible() || (get_tab_count() > 1 && (total_tabs_width + theme_cache.side_margin) > get_size().width))) {
tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
tab_bar->set_offset(SIDE_RIGHT, -buttons_offset);
} else {
tab_bar->set_offset(SIDE_RIGHT, -theme_cache.side_margin);
}
Expand All @@ -314,7 +338,7 @@ Vector<Control *> TabContainer::_get_tab_controls() const {
Vector<Control *> controls;
for (int i = 0; i < get_child_count(); i++) {
Control *control = Object::cast_to<Control>(get_child(i));
if (!control || control->is_set_as_top_level() || control == tab_bar || children_removing.has(control)) {
if (!control || control->is_set_as_top_level() || control == tab_bar || control == buttons_container || children_removing.has(control)) {
continue;
}

Expand Down Expand Up @@ -500,10 +524,18 @@ void TabContainer::_refresh_tab_names() {
}
}

void TabContainer::_on_buttons_container_child_order_changed() {
queue_redraw();
_update_margins();
if (!get_clip_tabs()) {
update_minimum_size();
}
}

void TabContainer::add_child_notify(Node *p_child) {
Container::add_child_notify(p_child);

if (p_child == tab_bar) {
if (p_child == tab_bar || p_child == buttons_container) {
return;
}

Expand Down Expand Up @@ -531,7 +563,7 @@ void TabContainer::add_child_notify(Node *p_child) {
void TabContainer::move_child_notify(Node *p_child) {
Container::move_child_notify(p_child);

if (p_child == tab_bar) {
if (p_child == tab_bar || p_child == buttons_container) {
return;
}

Expand All @@ -555,7 +587,7 @@ void TabContainer::move_child_notify(Node *p_child) {
void TabContainer::remove_child_notify(Node *p_child) {
Container::remove_child_notify(p_child);

if (p_child == tab_bar) {
if (p_child == tab_bar || p_child == buttons_container) {
return;
}

Expand Down Expand Up @@ -804,6 +836,10 @@ Size2 TabContainer::get_minimum_size() const {
ms.x += theme_cache.menu_icon->get_width();
}

if (buttons_container->get_child_count() > 0) {
ms.x += buttons_container->get_size().x;
}

if (theme_cache.side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
(get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) {
ms.x += theme_cache.side_margin;
Expand Down Expand Up @@ -869,6 +905,30 @@ Popup *TabContainer::get_popup() const {
return nullptr;
}

void TabContainer::add_button(Control *p_button) {
buttons_container->add_child(p_button, false);

queue_redraw();
_update_margins();
if (!get_clip_tabs()) {
update_minimum_size();
}
}

void TabContainer::remove_button(Control *p_button) {
if (p_button && p_button->get_parent() != buttons_container) {
return;
}

buttons_container->remove_child(p_button);

queue_redraw();
_update_margins();
if (!get_clip_tabs()) {
update_minimum_size();
}
}

void TabContainer::set_drag_to_rearrange_enabled(bool p_enabled) {
drag_to_rearrange_enabled = p_enabled;
}
Expand Down Expand Up @@ -937,6 +997,8 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_idx_from_control", "control"), &TabContainer::get_tab_idx_from_control);
ClassDB::bind_method(D_METHOD("set_popup", "popup"), &TabContainer::set_popup);
ClassDB::bind_method(D_METHOD("get_popup"), &TabContainer::get_popup);
ClassDB::bind_method(D_METHOD("add_button", "button"), &TabContainer::add_button);
ClassDB::bind_method(D_METHOD("remove_button", "button"), &TabContainer::remove_button);
ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabContainer::set_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabContainer::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabContainer::set_tabs_rearrange_group);
Expand All @@ -962,6 +1024,7 @@ void TabContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size");

BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, side_margin);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, buttons_separation);

BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, panel_style, "panel");
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, tabbar_style, "tabbar_background");
Expand Down Expand Up @@ -1007,5 +1070,9 @@ TabContainer::TabContainer() {
tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected));
tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed));

buttons_container = memnew(HBoxContainer);
add_child(buttons_container, false, INTERNAL_MODE_BACK);
buttons_container->connect("child_order_changed", callable_mp(this, &TabContainer::_on_buttons_container_child_order_changed));

connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}
10 changes: 10 additions & 0 deletions scene/gui/tab_container.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include "scene/gui/popup.h"
#include "scene/gui/tab_bar.h"

class Button;
class HBoxContainer;

class TabContainer : public Container {
GDCLASS(TabContainer, Container);

Expand All @@ -43,13 +46,15 @@ class TabContainer : public Container {
bool all_tabs_in_front = false;
bool menu_hovered = false;
mutable ObjectID popup_obj_id;
HBoxContainer *buttons_container = nullptr;
bool drag_to_rearrange_enabled = false;
bool use_hidden_tabs_for_min_size = false;
bool theme_changing = false;
Vector<Control *> children_removing;

struct ThemeCache {
int side_margin = 0;
int buttons_separation = 4;

Ref<StyleBox> panel_style;
Ref<StyleBox> tabbar_style;
Expand Down Expand Up @@ -97,6 +102,8 @@ class TabContainer : public Container {
void _on_tab_selected(int p_tab);
void _on_tab_button_pressed(int p_tab);

void _on_buttons_container_child_order_changed();

Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control);
bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const;
void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control);
Expand Down Expand Up @@ -157,6 +164,9 @@ class TabContainer : public Container {
void set_popup(Node *p_popup);
Popup *get_popup() const;

void add_button(Control *p_button);
void remove_button(Control *p_button);

void set_drag_to_rearrange_enabled(bool p_enabled);
bool get_drag_to_rearrange_enabled() const;
void set_tabs_rearrange_group(int p_group_id);
Expand Down
1 change: 1 addition & 0 deletions scene/theme/default_theme.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("drop_mark_color", "TabContainer", Color(1, 1, 1));

theme->set_constant("side_margin", "TabContainer", Math::round(8 * scale));
theme->set_constant("buttons_separation", "TabContainer", Math::round(4 * scale));
theme->set_constant("icon_separation", "TabContainer", Math::round(4 * scale));
theme->set_constant("icon_max_width", "TabContainer", 0);
theme->set_constant("outline_size", "TabContainer", 0);
Expand Down

0 comments on commit 5c4a414

Please sign in to comment.