summaryrefslogtreecommitdiff
path: root/scene/gui/tab_container.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/tab_container.cpp')
-rw-r--r--scene/gui/tab_container.cpp1167
1 files changed, 434 insertions, 733 deletions
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index c3fc08731e..8299d73b68 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -30,45 +30,17 @@
#include "tab_container.h"
-#include "core/object/message_queue.h"
-#include "core/string/translation.h"
-
#include "scene/gui/box_container.h"
#include "scene/gui/label.h"
#include "scene/gui/texture_rect.h"
int TabContainer::_get_top_margin() const {
- if (!tabs_visible) {
- return 0;
- }
-
- // Respect the minimum tab height.
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-
- int tab_height = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
-
- // Font height or higher icon wins.
- int content_height = 0;
-
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- content_height = MAX(content_height, text_buf[i]->get_size().y);
-
- Control *c = tabs[i];
- if (!c->has_meta("_tab_icon")) {
- continue;
- }
-
- Ref<Texture2D> tex = c->get_meta("_tab_icon");
- if (!tex.is_valid()) {
- continue;
- }
- content_height = MAX(content_height, tex->get_size().height);
+ int height = 0;
+ if (tabs_visible && get_tab_count() > 0) {
+ height = tab_bar->get_minimum_size().height;
}
- return tab_height + content_height;
+ return height;
}
void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
@@ -114,77 +86,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
return;
}
}
-
- // Do not activate tabs when tabs is empty.
- if (get_tab_count() == 0) {
- return;
- }
-
- Vector<Control *> tabs = _get_tabs();
-
- // Handle navigation buttons.
- if (buttons_visible_cache) {
- int popup_ofs = 0;
- if (popup) {
- popup_ofs = menu->get_width();
- }
-
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- if (is_layout_rtl()) {
- if (pos.x < popup_ofs + decrement->get_width()) {
- if (last_tab_cache < tabs.size() - 1) {
- first_tab_cache += 1;
- update();
- }
- return;
- } else if (pos.x < popup_ofs + increment->get_width() + decrement->get_width()) {
- if (first_tab_cache > 0) {
- first_tab_cache -= 1;
- update();
- }
- return;
- }
- } else {
- if (pos.x > size.width - increment->get_width() - popup_ofs && pos.x) {
- if (last_tab_cache < tabs.size() - 1) {
- first_tab_cache += 1;
- update();
- }
- return;
- } else if (pos.x > size.width - increment->get_width() - decrement->get_width() - popup_ofs) {
- if (first_tab_cache > 0) {
- first_tab_cache -= 1;
- update();
- }
- return;
- }
- }
- }
-
- // Activate the clicked tab.
- if (is_layout_rtl()) {
- pos.x = size.width - pos.x;
- }
-
- if (pos.x < tabs_ofs_cache) {
- return;
- }
-
- pos.x -= tabs_ofs_cache;
- for (int i = first_tab_cache; i <= last_tab_cache; i++) {
- if (get_tab_hidden(i)) {
- continue;
- }
- int tab_width = _get_tab_width(i);
- if (pos.x < tab_width) {
- if (!get_tab_disabled(i)) {
- set_current_tab(i);
- }
- break;
- }
- pos.x -= tab_width;
- }
}
Ref<InputEventMouseMotion> mm = p_event;
@@ -195,9 +96,8 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
// Mouse must be on tabs in the tab header area.
if (pos.y > _get_top_margin()) {
- if (menu_hovered || highlight_arrow > -1) {
+ if (menu_hovered) {
menu_hovered = false;
- highlight_arrow = -1;
update();
}
return;
@@ -209,7 +109,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x <= menu->get_width()) {
if (!menu_hovered) {
menu_hovered = true;
- highlight_arrow = -1;
update();
return;
}
@@ -221,7 +120,6 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x >= size.width - menu->get_width()) {
if (!menu_hovered) {
menu_hovered = true;
- highlight_arrow = -1;
update();
return;
}
@@ -235,101 +133,26 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) {
return;
}
}
-
- // Do not activate tabs when tabs is empty.
- if ((get_tab_count() == 0 || !buttons_visible_cache) && menu_hovered) {
- highlight_arrow = -1;
- update();
- return;
- }
-
- int popup_ofs = 0;
- if (popup) {
- popup_ofs = menu->get_width();
- }
-
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
-
- if (is_layout_rtl()) {
- if (pos.x <= popup_ofs + decrement->get_width()) {
- if (highlight_arrow != 1) {
- highlight_arrow = 1;
- update();
- }
- } else if (pos.x <= popup_ofs + increment->get_width() + decrement->get_width()) {
- if (highlight_arrow != 0) {
- highlight_arrow = 0;
- update();
- }
- } else if (highlight_arrow > -1) {
- highlight_arrow = -1;
- update();
- }
- } else {
- if (pos.x >= size.width - increment->get_width() - popup_ofs) {
- if (highlight_arrow != 1) {
- highlight_arrow = 1;
- update();
- }
- } else if (pos.x >= size.width - increment->get_width() - decrement->get_width() - popup_ofs) {
- if (highlight_arrow != 0) {
- highlight_arrow = 0;
- update();
- }
- } else if (highlight_arrow > -1) {
- highlight_arrow = -1;
- update();
- }
- }
}
}
void TabContainer::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_RESIZED: {
- Vector<Control *> tabs = _get_tabs();
- int side_margin = get_theme_constant(SNAME("side_margin"));
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- int header_width = get_size().width - side_margin * 2;
-
- // Find the width of the header area.
- Popup *popup = get_popup();
- if (popup) {
- header_width -= menu->get_width();
- }
- if (buttons_visible_cache) {
- header_width -= increment->get_width() + decrement->get_width();
+ case NOTIFICATION_ENTER_TREE: {
+ // If some nodes happen to be renamed outside the tree, the tab names need to be updated manually.
+ if (get_tab_count() > 0) {
+ _refresh_tab_names();
}
- if (popup || buttons_visible_cache) {
- header_width += side_margin;
- }
-
- // Find the width of all tabs after first_tab_cache.
- int all_tabs_width = 0;
- for (int i = first_tab_cache; i < tabs.size(); i++) {
- int tab_width = _get_tab_width(i);
- all_tabs_width += tab_width;
- }
-
- // Check if tabs before first_tab_cache would fit into the header area.
- for (int i = first_tab_cache - 1; i >= 0; i--) {
- int tab_width = _get_tab_width(i);
-
- if (all_tabs_width + tab_width > header_width) {
- break;
- }
+ } break;
- all_tabs_width += tab_width;
- first_tab_cache--;
- }
+ case NOTIFICATION_READY:
+ case NOTIFICATION_RESIZED: {
+ _update_margins();
} break;
+
case NOTIFICATION_DRAW: {
RID canvas = get_canvas_item();
Size2 size = get_size();
- bool rtl = is_layout_rtl();
// Draw only the tab area if the header is hidden.
Ref<StyleBox> panel = get_theme_stylebox(SNAME("panel"));
@@ -338,480 +161,173 @@ void TabContainer::_notification(int p_what) {
return;
}
- Vector<Control *> tabs = _get_tabs();
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> increment_hl = get_theme_icon(SNAME("increment_highlight"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- Ref<Texture2D> decrement_hl = get_theme_icon(SNAME("decrement_highlight"));
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
- Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
- Color font_unselected_color = get_theme_color(SNAME("font_unselected_color"));
- Color font_disabled_color = get_theme_color(SNAME("font_disabled_color"));
- int side_margin = get_theme_constant(SNAME("side_margin"));
-
- // Find out start and width of the header area.
- int header_x = side_margin;
- int header_width = size.width - side_margin * 2;
int header_height = _get_top_margin();
- Popup *popup = get_popup();
- if (popup) {
- header_width -= menu->get_width();
- }
-
- // Check if all tabs would fit into the header area.
- int all_tabs_width = 0;
- for (int i = 0; i < tabs.size(); i++) {
- if (get_tab_hidden(i)) {
- continue;
- }
- int tab_width = _get_tab_width(i);
- all_tabs_width += tab_width;
-
- if (all_tabs_width > header_width) {
- // Not all tabs are visible at the same time - reserve space for navigation buttons.
- buttons_visible_cache = true;
- header_width -= decrement->get_width() + increment->get_width();
- break;
- } else {
- buttons_visible_cache = false;
- }
- }
- // With buttons, a right side margin does not need to be respected.
- if (popup || buttons_visible_cache) {
- header_width += side_margin;
- }
- if (!buttons_visible_cache) {
- first_tab_cache = 0;
- }
-
- // Go through the visible tabs to find the width they occupy.
- all_tabs_width = 0;
- Vector<int> tab_widths;
- for (int i = first_tab_cache; i < tabs.size(); i++) {
- if (get_tab_hidden(i)) {
- tab_widths.push_back(0);
- continue;
- }
- int tab_width = _get_tab_width(i);
- if (all_tabs_width + tab_width > header_width && tab_widths.size() > 0) {
- break;
- }
- all_tabs_width += tab_width;
- tab_widths.push_back(tab_width);
- }
-
- // Find the offset at which to draw tabs, according to the alignment.
- switch (alignment) {
- case ALIGNMENT_LEFT:
- tabs_ofs_cache = header_x;
- break;
- case ALIGNMENT_CENTER:
- tabs_ofs_cache = header_x + (header_width / 2) - (all_tabs_width / 2);
- break;
- case ALIGNMENT_RIGHT:
- tabs_ofs_cache = header_x + header_width - all_tabs_width;
- break;
- }
-
- if (all_tabs_in_front) {
- // Draw the tab area.
- panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- }
-
- // Draw unselected tabs in back
- int x = 0;
- int x_current = 0;
- int index = 0;
- for (int i = 0; i < tab_widths.size(); i++) {
- index = i + first_tab_cache;
- if (get_tab_hidden(index)) {
- continue;
- }
-
- int tab_width = tab_widths[i];
- if (index == current) {
- x_current = x;
- } else if (get_tab_disabled(index)) {
- if (rtl) {
- _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
- } else {
- _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x);
- }
- } else {
- if (rtl) {
- _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width);
- } else {
- _draw_tab(tab_unselected, font_unselected_color, index, tabs_ofs_cache + x);
- }
- }
-
- x += tab_width;
- last_tab_cache = index;
- }
-
- if (!all_tabs_in_front) {
- // Draw the tab area.
- panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- }
-
- // Draw selected tab in front. Only draw selected tab when it's in visible range.
- if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) {
- Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected;
- if (rtl) {
- _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]);
- } else {
- _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current);
- }
- }
+ panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
// Draw the popup menu.
- if (rtl) {
- x = 0;
- } else {
- x = get_size().width;
- }
- if (popup) {
- if (!rtl) {
- x -= menu->get_width();
- }
- if (menu_hovered) {
- menu_hl->draw(get_canvas_item(), Size2(x, (header_height - menu_hl->get_height()) / 2));
- } else {
- menu->draw(get_canvas_item(), Size2(x, (header_height - menu->get_height()) / 2));
- }
- if (rtl) {
- x += menu->get_width();
- }
- }
+ if (get_popup()) {
+ Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
+ Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
- // Draw the navigation buttons.
- if (buttons_visible_cache) {
- if (rtl) {
- if (last_tab_cache < tabs.size() - 1) {
- draw_texture(highlight_arrow == 1 ? decrement_hl : decrement, Point2(x, (header_height - increment->get_height()) / 2));
- } else {
- draw_texture(decrement, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
- x += increment->get_width();
+ int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width();
- if (first_tab_cache > 0) {
- draw_texture(highlight_arrow == 0 ? increment_hl : increment, Point2(x, (header_height - decrement->get_height()) / 2));
- } else {
- draw_texture(increment, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
- x += decrement->get_width();
+ if (menu_hovered) {
+ menu_hl->draw(get_canvas_item(), Point2(x, (header_height - menu_hl->get_height()) / 2));
} else {
- x -= increment->get_width();
- if (last_tab_cache < tabs.size() - 1) {
- draw_texture(highlight_arrow == 1 ? increment_hl : increment, Point2(x, (header_height - increment->get_height()) / 2));
- } else {
- draw_texture(increment, Point2(x, (header_height - increment->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
-
- x -= decrement->get_width();
- if (first_tab_cache > 0) {
- draw_texture(highlight_arrow == 0 ? decrement_hl : decrement, Point2(x, (header_height - decrement->get_height()) / 2));
- } else {
- draw_texture(decrement, Point2(x, (header_height - decrement->get_height()) / 2), Color(1, 1, 1, 0.5));
- }
+ menu->draw(get_canvas_item(), Point2(x, (header_height - menu->get_height()) / 2));
}
}
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- text_buf.write[i]->clear();
- }
- _theme_changing = true;
+ theme_changing = true;
call_deferred(SNAME("_on_theme_changed")); // Wait until all changed theme.
} break;
}
}
-void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
- Control *control = get_tab_control(p_index);
- RID canvas = get_canvas_item();
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- int icon_text_distance = get_theme_constant(SNAME("icon_separation"));
- int tab_width = _get_tab_width(p_index);
- int header_height = _get_top_margin();
-
- // Draw the tab background.
- Rect2 tab_rect(p_x, 0, tab_width, header_height);
- p_tab_style->draw(canvas, tab_rect);
-
- // Draw the tab contents.
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
-
- int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT);
- int top_margin = p_tab_style->get_margin(SIDE_TOP);
- int y_center = top_margin + (tab_rect.size.y - p_tab_style->get_minimum_size().y) / 2;
-
- // Draw the tab icon.
- if (control->has_meta("_tab_icon")) {
- Ref<Texture2D> icon = control->get_meta("_tab_icon");
- if (icon.is_valid()) {
- int y = y_center - (icon->get_height() / 2);
- icon->draw(canvas, Point2i(x_content, y));
- if (!text.is_empty()) {
- x_content += icon->get_width() + icon_text_distance;
- }
- }
- }
-
- // Draw the tab text.
- Point2i text_pos(x_content, y_center - text_buf[p_index]->get_size().y / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- text_buf[p_index]->draw_outline(canvas, text_pos, outline_size, font_outline_color);
- }
- text_buf[p_index]->draw(canvas, text_pos, p_font_color);
-}
-
-void TabContainer::_refresh_texts() {
- text_buf.clear();
- Vector<Control *> tabs = _get_tabs();
- bool rtl = is_layout_rtl();
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- for (int i = 0; i < tabs.size(); i++) {
- Control *control = Object::cast_to<Control>(tabs[i]);
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
-
- Ref<TextLine> name;
- name.instantiate();
- name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
- name->add_string(text, font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
- text_buf.push_back(name);
- }
-}
-
void TabContainer::_on_theme_changed() {
- if (!_theme_changing) {
+ if (!theme_changing) {
return;
}
- _refresh_texts();
-
- update_minimum_size();
+ tab_bar->add_theme_style_override(SNAME("tab_unselected"), get_theme_stylebox(SNAME("tab_unselected")));
+ tab_bar->add_theme_style_override(SNAME("tab_selected"), get_theme_stylebox(SNAME("tab_selected")));
+ tab_bar->add_theme_style_override(SNAME("tab_disabled"), get_theme_stylebox(SNAME("tab_disabled")));
+ tab_bar->add_theme_icon_override(SNAME("increment"), get_theme_icon(SNAME("increment")));
+ tab_bar->add_theme_icon_override(SNAME("increment_highlight"), get_theme_icon(SNAME("increment_highlight")));
+ tab_bar->add_theme_icon_override(SNAME("decrement"), get_theme_icon(SNAME("decrement")));
+ tab_bar->add_theme_icon_override(SNAME("decrement_highlight"), get_theme_icon(SNAME("decrement_highlight")));
+ tab_bar->add_theme_icon_override(SNAME("drop_mark"), get_theme_icon(SNAME("drop_mark")));
+ tab_bar->add_theme_color_override(SNAME("drop_mark_color"), get_theme_color(SNAME("drop_mark_color")));
+ tab_bar->add_theme_color_override(SNAME("font_selected_color"), get_theme_color(SNAME("font_selected_color")));
+ tab_bar->add_theme_color_override(SNAME("font_unselected_color"), get_theme_color(SNAME("font_unselected_color")));
+ tab_bar->add_theme_color_override(SNAME("font_disabled_color"), get_theme_color(SNAME("font_disabled_color")));
+ tab_bar->add_theme_color_override(SNAME("font_outline_color"), get_theme_color(SNAME("font_outline_color")));
+ tab_bar->add_theme_font_override(SNAME("font"), get_theme_font(SNAME("font")));
+ tab_bar->add_theme_font_size_override(SNAME("font_size"), get_theme_font_size(SNAME("font_size")));
+ tab_bar->add_theme_constant_override(SNAME("h_separation"), get_theme_constant(SNAME("icon_separation")));
+ tab_bar->add_theme_constant_override(SNAME("outline_size"), get_theme_constant(SNAME("outline_size")));
+
+ _update_margins();
if (get_tab_count() > 0) {
_repaint();
- update();
+ } else {
+ update_minimum_size();
}
- _theme_changing = false;
+ update();
+
+ theme_changing = false;
}
void TabContainer::_repaint() {
Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ Vector<Control *> controls = _get_tab_controls();
+ int current = get_current_tab();
+
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
+
if (i == current) {
c->show();
c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
+
if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
}
+
c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + sb->get_margin(SIDE_TOP));
c->set_offset(SIDE_LEFT, c->get_offset(SIDE_LEFT) + sb->get_margin(SIDE_LEFT));
c->set_offset(SIDE_RIGHT, c->get_offset(SIDE_RIGHT) - sb->get_margin(SIDE_RIGHT));
c->set_offset(SIDE_BOTTOM, c->get_offset(SIDE_BOTTOM) - sb->get_margin(SIDE_BOTTOM));
-
} else {
c->hide();
}
}
-}
-
-void TabContainer::_on_mouse_exited() {
- if (menu_hovered || highlight_arrow > -1) {
- menu_hovered = false;
- highlight_arrow = -1;
- update();
- }
-}
-
-int TabContainer::_get_tab_width(int p_index) const {
- ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0);
- Control *control = get_tab_control(p_index);
- if (!control || get_tab_hidden(p_index)) {
- return 0;
- }
-
- // Get the width of the text displayed on the tab.
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name()));
- int width = font->get_string_size(text, font_size).width;
-
- // Add space for a tab icon.
- if (control->has_meta("_tab_icon")) {
- Ref<Texture2D> icon = control->get_meta("_tab_icon");
- if (icon.is_valid()) {
- width += icon->get_width();
- if (!text.is_empty()) {
- width += get_theme_constant(SNAME("icon_separation"));
- }
- }
- }
-
- // Respect a minimum size.
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
- if (get_tab_disabled(p_index)) {
- width += tab_disabled->get_minimum_size().width;
- } else if (p_index == current) {
- width += tab_selected->get_minimum_size().width;
- } else {
- width += tab_unselected->get_minimum_size().width;
- }
- return width;
+ update_minimum_size();
}
-Vector<Control *> TabContainer::_get_tabs() 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()) {
- continue;
- }
-
- controls.push_back(control);
- }
- return controls;
-}
+void TabContainer::_update_margins() {
+ int menu_width = get_theme_icon(SNAME("menu"))->get_width();
+ int side_margin = get_theme_constant(SNAME("side_margin"));
-void TabContainer::_child_renamed_callback() {
- _refresh_texts();
- update();
-}
+ // Directly check for validity, to avoid errors when quitting.
+ bool has_popup = popup_obj_id.is_valid();
-void TabContainer::add_child_notify(Node *p_child) {
- Container::add_child_notify(p_child);
+ if (get_tab_count() == 0) {
+ tab_bar->set_offset(SIDE_LEFT, 0);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
return;
}
- _refresh_texts();
- call_deferred("_repaint");
- update();
-
- bool first = (_get_tabs().size() == 1);
- if (first) {
- current = 0;
- previous = 0;
- }
-
- p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
- if (first && is_inside_tree()) {
- emit_signal(SNAME("tab_changed"), current);
- }
-}
-
-void TabContainer::move_child_notify(Node *p_child) {
- Container::move_child_notify(p_child);
-
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
- return;
- }
+ switch (get_tab_alignment()) {
+ case TabBar::ALIGNMENT_LEFT: {
+ tab_bar->set_offset(SIDE_LEFT, side_margin);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } break;
- _update_current_tab();
- update();
-}
+ case TabBar::ALIGNMENT_CENTER: {
+ tab_bar->set_offset(SIDE_LEFT, 0);
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } break;
-int TabContainer::get_tab_count() const {
- return _get_tabs().size();
-}
+ case TabBar::ALIGNMENT_RIGHT: {
+ tab_bar->set_offset(SIDE_LEFT, 0);
-void TabContainer::set_current_tab(int p_current) {
- ERR_FAIL_INDEX(p_current, get_tab_count());
+ if (has_popup) {
+ tab_bar->set_offset(SIDE_RIGHT, -menu_width);
+ return;
+ }
- int pending_previous = current;
- current = p_current;
+ int first_tab_pos = tab_bar->get_tab_rect(0).position.x;
+ Rect2 last_tab_rect = tab_bar->get_tab_rect(get_tab_count() - 1);
+ int total_tabs_width = last_tab_rect.position.x - first_tab_pos + last_tab_rect.size.width;
- _repaint();
+ // 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 + side_margin) > get_size().width))) {
+ tab_bar->set_offset(SIDE_RIGHT, has_popup ? -menu_width : 0);
+ } else {
+ tab_bar->set_offset(SIDE_RIGHT, -side_margin);
+ }
+ } break;
- if (pending_previous == current) {
- emit_signal(SNAME("tab_selected"), current);
- } else {
- previous = pending_previous;
- emit_signal(SNAME("tab_selected"), current);
- emit_signal(SNAME("tab_changed"), current);
+ case TabBar::ALIGNMENT_MAX:
+ break; // Can't happen, but silences warning.
}
-
- update();
-}
-
-int TabContainer::get_current_tab() const {
- return current;
-}
-
-int TabContainer::get_previous_tab() const {
- return previous;
}
-Control *TabContainer::get_tab_control(int p_idx) const {
- Vector<Control *> tabs = _get_tabs();
- if (p_idx >= 0 && p_idx < tabs.size()) {
- return tabs[p_idx];
- } else {
- return nullptr;
+void TabContainer::_on_mouse_exited() {
+ if (menu_hovered) {
+ menu_hovered = false;
+ update();
}
}
-Control *TabContainer::get_current_tab_control() const {
- return get_tab_control(current);
-}
-
-void TabContainer::remove_child_notify(Node *p_child) {
- Container::remove_child_notify(p_child);
+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) {
+ continue;
+ }
- Control *c = Object::cast_to<Control>(p_child);
- if (!c || c->is_set_as_top_level()) {
- return;
+ controls.push_back(control);
}
- // Defer the call because tab is not yet removed (remove_child_notify is called right before p_child is actually removed).
- call_deferred(SNAME("_update_current_tab"));
-
- p_child->disconnect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
-
- update();
-}
-
-void TabContainer::_update_current_tab() {
- _refresh_texts();
-
- int tc = get_tab_count();
- if (current >= tc) {
- current = tc - 1;
- }
- if (current < 0) {
- current = 0;
- } else {
- set_current_tab(current);
- }
+ return controls;
}
-Variant TabContainer::get_drag_data(const Point2 &p_point) {
+Variant TabContainer::_get_drag_data_fw(const Point2 &p_point, Control *p_from_control) {
if (!drag_to_rearrange_enabled) {
return Variant();
}
int tab_over = get_tab_idx_at_point(p_point);
-
if (tab_over < 0) {
return Variant();
}
@@ -824,18 +340,20 @@ Variant TabContainer::get_drag_data(const Point2 &p_point) {
tf->set_texture(icon);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(get_tab_title(tab_over)));
- drag_preview->add_child(label);
set_drag_preview(drag_preview);
+ drag_preview->add_child(label);
Dictionary drag_data;
drag_data["type"] = "tabc_element";
drag_data["tabc_element"] = tab_over;
drag_data["from_path"] = get_path();
+
return drag_data;
}
-bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+bool TabContainer::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const {
if (!drag_to_rearrange_enabled) {
return false;
}
@@ -851,7 +369,7 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c
if (from_path == to_path) {
return true;
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between other TabContainers
+ // Drag and drop between other TabContainers.
Node *from_node = get_node(from_path);
TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
@@ -859,16 +377,15 @@ bool TabContainer::can_drop_data(const Point2 &p_point, const Variant &p_data) c
}
}
}
+
return false;
}
-void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
+void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) {
if (!drag_to_rearrange_enabled) {
return;
}
- int hover_now = get_tab_idx_at_point(p_point);
-
Dictionary d = p_data;
if (!d.has("type")) {
return;
@@ -876,91 +393,240 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
if (String(d["type"]) == "tabc_element") {
int tab_from_id = d["tabc_element"];
+ int hover_now = get_tab_idx_at_point(p_point);
NodePath from_path = d["from_path"];
NodePath to_path = get_path();
+
if (from_path == to_path) {
- if (hover_now < 0) {
- hover_now = get_tab_count() - 1;
+ if (tab_from_id == hover_now) {
+ return;
+ }
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = tab_bar->get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) {
+ if (hover_now > tab_from_id) {
+ hover_now -= 1;
+ }
+ } else if (tab_from_id > hover_now) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1;
}
- move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index());
- set_current_tab(hover_now);
+
+ move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index(false));
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(hover_now);
+ }
+
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between TabContainers
+ // Drag and drop between TabContainers.
+
Node *from_node = get_node(from_path);
TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node);
+
if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
+ // Get the tab properties before they get erased by the child removal.
+ String tab_title = from_tabc->get_tab_title(tab_from_id);
+ bool tab_disabled = from_tabc->is_tab_disabled(tab_from_id);
+
+ // Drop the new tab to the left or right depending on where the target tab is being hovered.
+ if (hover_now != -1) {
+ Rect2 tab_rect = tab_bar->get_tab_rect(hover_now);
+ if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) {
+ hover_now += 1;
+ }
+ } else {
+ hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count();
+ }
+
Control *moving_tabc = from_tabc->get_tab_control(tab_from_id);
from_tabc->remove_child(moving_tabc);
- add_child(moving_tabc, false, INTERNAL_MODE_FRONT);
- if (hover_now < 0) {
- hover_now = get_tab_count() - 1;
+ add_child(moving_tabc, true);
+
+ set_tab_title(get_tab_count() - 1, tab_title);
+ set_tab_disabled(get_tab_count() - 1, tab_disabled);
+
+ move_child(moving_tabc, get_tab_control(hover_now)->get_index(false));
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(hover_now);
}
- move_child(moving_tabc, get_tab_control(hover_now)->get_index());
- set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
}
}
}
- update();
}
-int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
- if (get_tab_count() == 0) {
- return -1;
+void TabContainer::_on_tab_changed(int p_tab) {
+ call_deferred(SNAME("_repaint"));
+
+ emit_signal(SNAME("tab_changed"), p_tab);
+}
+
+void TabContainer::_on_tab_selected(int p_tab) {
+ if (p_tab != get_previous_tab()) {
+ call_deferred(SNAME("_repaint"));
+ }
+
+ emit_signal(SNAME("tab_selected"), p_tab);
+}
+
+void TabContainer::_on_tab_button_pressed(int p_tab) {
+ emit_signal(SNAME("tab_button_pressed"), p_tab);
+}
+
+void TabContainer::_refresh_tab_names() {
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ if (!controls[i]->has_meta("_tab_name") && String(controls[i]->get_name()) != get_tab_title(i)) {
+ tab_bar->set_tab_title(i, controls[i]->get_name());
+ }
}
+}
- // must be on tabs in the tab header area.
- if (p_point.y > _get_top_margin()) {
- return -1;
+void TabContainer::add_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
}
- Size2 size = get_size();
- int button_ofs = 0;
- int px = p_point.x;
+ Container::add_child_notify(p_child);
- if (is_layout_rtl()) {
- px = size.width - px;
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
+ return;
}
+ c->hide();
+
+ tab_bar->add_tab(p_child->get_name());
- if (px < tabs_ofs_cache) {
- return -1;
+ _update_margins();
+ if (get_tab_count() == 1) {
+ update();
}
- Popup *popup = get_popup();
- if (popup) {
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- button_ofs += menu->get_width();
+ p_child->connect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
+
+ // TabBar won't emit the "tab_changed" signal when not inside the tree.
+ if (!is_inside_tree()) {
+ call_deferred("_repaint");
}
- if (buttons_visible_cache) {
- Ref<Texture2D> increment = get_theme_icon(SNAME("increment"));
- Ref<Texture2D> decrement = get_theme_icon(SNAME("decrement"));
- button_ofs += increment->get_width() + decrement->get_width();
+}
+
+void TabContainer::move_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
}
- if (px > size.width - button_ofs) {
- return -1;
+
+ Container::move_child_notify(p_child);
+
+ Control *c = Object::cast_to<Control>(p_child);
+ if (c && !c->is_set_as_top_level()) {
+ int old_idx = -1;
+ String tab_name = String(c->get_meta("_tab_name", c->get_name()));
+
+ // Find the previous tab index of the control.
+ for (int i = 0; i < get_tab_count(); i++) {
+ if (get_tab_title(i) == tab_name) {
+ old_idx = i;
+ break;
+ }
+ }
+
+ tab_bar->move_tab(old_idx, get_tab_idx_from_control(c));
}
+}
- // get the tab at the point
- Vector<Control *> tabs = _get_tabs();
- px -= tabs_ofs_cache;
- for (int i = first_tab_cache; i <= last_tab_cache; i++) {
- int tab_width = _get_tab_width(i);
- if (px < tab_width) {
+void TabContainer::remove_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
+ }
+
+ Container::remove_child_notify(p_child);
+
+ Control *c = Object::cast_to<Control>(p_child);
+ if (!c || c->is_set_as_top_level()) {
+ return;
+ }
+
+ tab_bar->remove_tab(get_tab_idx_from_control(c));
+
+ _update_margins();
+ if (get_tab_count() == 0) {
+ update();
+ }
+
+ p_child->remove_meta("_tab_name");
+ p_child->disconnect("renamed", callable_mp(this, &TabContainer::_refresh_tab_names));
+
+ // TabBar won't emit the "tab_changed" signal when not inside the tree.
+ if (!is_inside_tree()) {
+ call_deferred("_repaint");
+ }
+}
+
+int TabContainer::get_tab_count() const {
+ return tab_bar->get_tab_count();
+}
+
+void TabContainer::set_current_tab(int p_current) {
+ tab_bar->set_current_tab(p_current);
+}
+
+int TabContainer::get_current_tab() const {
+ return tab_bar->get_current_tab();
+}
+
+int TabContainer::get_previous_tab() const {
+ return tab_bar->get_previous_tab();
+}
+
+Control *TabContainer::get_tab_control(int p_idx) const {
+ Vector<Control *> controls = _get_tab_controls();
+ if (p_idx >= 0 && p_idx < controls.size()) {
+ return controls[p_idx];
+ } else {
+ return nullptr;
+ }
+}
+
+Control *TabContainer::get_current_tab_control() const {
+ return get_tab_control(tab_bar->get_current_tab());
+}
+
+int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
+ return tab_bar->get_tab_idx_at_point(p_point);
+}
+
+int TabContainer::get_tab_idx_from_control(Control *p_child) const {
+ ERR_FAIL_NULL_V(p_child, -1);
+ ERR_FAIL_COND_V(p_child->get_parent() != this, -1);
+
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ if (controls[i] == p_child) {
return i;
}
- px -= tab_width;
}
+
return -1;
}
-void TabContainer::set_tab_alignment(AlignmentMode p_alignment) {
- ERR_FAIL_INDEX(p_alignment, 3);
- alignment = p_alignment;
- update();
+void TabContainer::set_tab_alignment(TabBar::AlignmentMode p_alignment) {
+ tab_bar->set_tab_alignment(p_alignment);
+ _update_margins();
+}
+
+TabBar::AlignmentMode TabContainer::get_tab_alignment() const {
+ return tab_bar->get_tab_alignment();
+}
+
+void TabContainer::set_clip_tabs(bool p_clip_tabs) {
+ tab_bar->set_clip_tabs(p_clip_tabs);
}
-TabContainer::AlignmentMode TabContainer::get_tab_alignment() const {
- return alignment;
+bool TabContainer::get_clip_tabs() const {
+ return tab_bar->get_clip_tabs();
}
void TabContainer::set_tabs_visible(bool p_visible) {
@@ -969,11 +635,12 @@ void TabContainer::set_tabs_visible(bool p_visible) {
}
tabs_visible = p_visible;
+ tab_bar->set_visible(tabs_visible);
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
- if (p_visible) {
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
+ if (tabs_visible) {
c->set_offset(SIDE_TOP, _get_top_margin());
} else {
c->set_offset(SIDE_TOP, 0);
@@ -995,7 +662,8 @@ void TabContainer::set_all_tabs_in_front(bool p_in_front) {
all_tabs_in_front = p_in_front;
- update();
+ remove_child(tab_bar);
+ add_child(tab_bar, false, all_tabs_in_front ? INTERNAL_MODE_FRONT : INTERNAL_MODE_BACK);
}
bool TabContainer::is_all_tabs_in_front() const {
@@ -1005,95 +673,87 @@ bool TabContainer::is_all_tabs_in_front() const {
void TabContainer::set_tab_title(int p_tab, const String &p_title) {
Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
- child->set_meta("_tab_name", p_title);
- _refresh_texts();
- update();
-}
-String TabContainer::get_tab_title(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, "");
- if (child->has_meta("_tab_name")) {
- return child->get_meta("_tab_name");
+ tab_bar->set_tab_title(p_tab, p_title);
+
+ if (p_title == child->get_name()) {
+ child->remove_meta("_tab_name");
} else {
- return child->get_name();
+ child->set_meta("_tab_name", p_title);
+ }
+
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
}
+String TabContainer::get_tab_title(int p_tab) const {
+ return tab_bar->get_tab_title(p_tab);
+}
+
void TabContainer::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND(!child);
- child->set_meta("_tab_icon", p_icon);
- update();
+ tab_bar->set_tab_icon(p_tab, p_icon);
+
+ _update_margins();
+ _repaint();
}
Ref<Texture2D> TabContainer::get_tab_icon(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, Ref<Texture2D>());
- if (child->has_meta("_tab_icon")) {
- return child->get_meta("_tab_icon");
- } else {
- return Ref<Texture2D>();
- }
+ return tab_bar->get_tab_icon(p_tab);
}
void TabContainer::set_tab_disabled(int p_tab, bool p_disabled) {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND(!child);
- child->set_meta("_tab_disabled", p_disabled);
- update();
-}
+ tab_bar->set_tab_disabled(p_tab, p_disabled);
-bool TabContainer::get_tab_disabled(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, false);
- if (child->has_meta("_tab_disabled")) {
- return child->get_meta("_tab_disabled");
- } else {
- return false;
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
}
+bool TabContainer::is_tab_disabled(int p_tab) const {
+ return tab_bar->is_tab_disabled(p_tab);
+}
+
void TabContainer::set_tab_hidden(int p_tab, bool p_hidden) {
Control *child = get_tab_control(p_tab);
ERR_FAIL_COND(!child);
- child->set_meta("_tab_hidden", p_hidden);
- update();
- for (int i = 0; i < get_tab_count(); i++) {
- int try_tab = (p_tab + 1 + i) % get_tab_count();
- if (get_tab_disabled(try_tab) || get_tab_hidden(try_tab)) {
- continue;
- }
- set_current_tab(try_tab);
- return;
+ tab_bar->set_tab_hidden(p_tab, p_hidden);
+ child->hide();
+
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
+}
- //assumed no other tab can be switched to, just hide
- child->hide();
+bool TabContainer::is_tab_hidden(int p_tab) const {
+ return tab_bar->is_tab_hidden(p_tab);
}
-bool TabContainer::get_tab_hidden(int p_tab) const {
- Control *child = get_tab_control(p_tab);
- ERR_FAIL_COND_V(!child, false);
- if (child->has_meta("_tab_hidden")) {
- return child->get_meta("_tab_hidden");
- } else {
- return false;
- }
+void TabContainer::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
+ tab_bar->set_tab_button_icon(p_tab, p_icon);
+
+ _update_margins();
+ _repaint();
+}
+
+Ref<Texture2D> TabContainer::get_tab_button_icon(int p_tab) const {
+ return tab_bar->get_tab_button_icon(p_tab);
}
void TabContainer::get_translatable_strings(List<String> *p_strings) const {
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ Vector<Control *> controls = _get_tab_controls();
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
if (!c->has_meta("_tab_name")) {
continue;
}
String name = c->get_meta("_tab_name");
-
if (!name.is_empty()) {
p_strings->push_back(name);
}
@@ -1103,9 +763,26 @@ void TabContainer::get_translatable_strings(List<String> *p_strings) const {
Size2 TabContainer::get_minimum_size() const {
Size2 ms;
- Vector<Control *> tabs = _get_tabs();
- for (int i = 0; i < tabs.size(); i++) {
- Control *c = tabs[i];
+ if (tabs_visible) {
+ ms = tab_bar->get_minimum_size();
+
+ if (!get_clip_tabs()) {
+ if (get_popup()) {
+ ms.x += get_theme_icon(SNAME("menu"))->get_width();
+ }
+
+ int side_margin = get_theme_constant(SNAME("side_margin"));
+ if (side_margin > 0 && get_tab_alignment() != TabBar::ALIGNMENT_CENTER &&
+ (get_tab_alignment() != TabBar::ALIGNMENT_RIGHT || !get_popup())) {
+ ms.x += side_margin;
+ }
+ }
+ }
+
+ Vector<Control *> controls = _get_tab_controls();
+ int max_control_height = 0;
+ for (int i = 0; i < controls.size(); i++) {
+ Control *c = controls[i];
if (!c->is_visible_in_tree() && !use_hidden_tabs_for_min_size) {
continue;
@@ -1113,29 +790,30 @@ Size2 TabContainer::get_minimum_size() const {
Size2 cms = c->get_combined_minimum_size();
ms.x = MAX(ms.x, cms.x);
- ms.y = MAX(ms.y, cms.y);
+ max_control_height = MAX(max_control_height, cms.y);
}
+ ms.y += max_control_height;
- Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
- Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
- Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
-
- if (tabs_visible) {
- ms.y += MAX(MAX(tab_unselected->get_minimum_size().y, tab_selected->get_minimum_size().y), tab_disabled->get_minimum_size().y);
- ms.y += _get_top_margin();
- }
-
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- ms += sb->get_minimum_size();
+ Size2 panel_ms = get_theme_stylebox(SNAME("panel"))->get_minimum_size();
+ ms.x = MAX(ms.x, panel_ms.x);
+ ms.y += panel_ms.y;
return ms;
}
void TabContainer::set_popup(Node *p_popup) {
- ERR_FAIL_NULL(p_popup);
+ bool had_popup = get_popup();
+
Popup *popup = Object::cast_to<Popup>(p_popup);
popup_obj_id = popup ? popup->get_instance_id() : ObjectID();
- update();
+
+ if (had_popup != bool(popup)) {
+ update();
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
+ }
+ }
}
Popup *TabContainer::get_popup() const {
@@ -1150,6 +828,7 @@ Popup *TabContainer::get_popup() const {
popup_obj_id = ObjectID();
}
}
+
return nullptr;
}
@@ -1162,21 +841,30 @@ bool TabContainer::get_drag_to_rearrange_enabled() const {
}
void TabContainer::set_tabs_rearrange_group(int p_group_id) {
- tabs_rearrange_group = p_group_id;
+ tab_bar->set_tabs_rearrange_group(p_group_id);
}
int TabContainer::get_tabs_rearrange_group() const {
- return tabs_rearrange_group;
+ return tab_bar->get_tabs_rearrange_group();
}
void TabContainer::set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs) {
use_hidden_tabs_for_min_size = p_use_hidden_tabs;
+ update_minimum_size();
}
bool TabContainer::get_use_hidden_tabs_for_min_size() const {
return use_hidden_tabs_for_min_size;
}
+Vector<int> TabContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
+
+Vector<int> TabContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
+
void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabContainer::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabContainer::set_current_tab);
@@ -1186,6 +874,8 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control);
ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabContainer::set_tab_alignment);
ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabContainer::get_tab_alignment);
+ ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabContainer::set_clip_tabs);
+ ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabContainer::get_clip_tabs);
ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible);
ClassDB::bind_method(D_METHOD("are_tabs_visible"), &TabContainer::are_tabs_visible);
ClassDB::bind_method(D_METHOD("set_all_tabs_in_front", "is_front"), &TabContainer::set_all_tabs_in_front);
@@ -1195,40 +885,51 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabContainer::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabContainer::get_tab_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabContainer::set_tab_disabled);
- ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabContainer::get_tab_disabled);
+ ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabContainer::is_tab_disabled);
ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabContainer::set_tab_hidden);
- ClassDB::bind_method(D_METHOD("get_tab_hidden", "tab_idx"), &TabContainer::get_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabContainer::is_tab_hidden);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabContainer::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabContainer::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabContainer::get_tab_idx_at_point);
+ 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("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);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabContainer::get_tabs_rearrange_group);
-
ClassDB::bind_method(D_METHOD("set_use_hidden_tabs_for_min_size", "enabled"), &TabContainer::set_use_hidden_tabs_for_min_size);
ClassDB::bind_method(D_METHOD("get_use_hidden_tabs_for_min_size"), &TabContainer::get_use_hidden_tabs_for_min_size);
ClassDB::bind_method(D_METHOD("_repaint"), &TabContainer::_repaint);
ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed);
- ClassDB::bind_method(D_METHOD("_update_current_tab"), &TabContainer::_update_current_tab);
+ ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TabContainer::_get_drag_data_fw);
+ ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TabContainer::_can_drop_data_fw);
+ ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TabContainer::_drop_data_fw);
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("pre_popup_pressed"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_rearrange_group"), "set_tabs_rearrange_group", "get_tabs_rearrange_group");
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_ENUM_CONSTANT(ALIGNMENT_LEFT);
- BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
- BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT);
}
TabContainer::TabContainer() {
+ tab_bar = memnew(TabBar);
+ tab_bar->set_drag_forwarding(this);
+ add_child(tab_bar, false, INTERNAL_MODE_FRONT);
+ tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE);
+ tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed));
+ 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));
+
connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}