diff options
Diffstat (limited to 'scene/gui/tabs.cpp')
-rw-r--r-- | scene/gui/tabs.cpp | 510 |
1 files changed, 320 insertions, 190 deletions
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 1a3b53f489..da1a9698d0 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,44 +30,48 @@ #include "tabs.h" -#include "core/message_queue.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" Size2 Tabs::get_minimum_size() const { - - Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); - Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); + Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected"); + Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); - Size2 ms(0, MAX(MAX(tab_bg->get_minimum_size().height, tab_fg->get_minimum_size().height), tab_disabled->get_minimum_size().height) + font->get_height()); + int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height); - for (int i = 0; i < tabs.size(); i++) { + Size2 ms(0, 0); + for (int i = 0; i < tabs.size(); i++) { Ref<Texture2D> tex = tabs[i].icon; if (tex.is_valid()) { ms.height = MAX(ms.height, tex->get_size().height); - if (tabs[i].text != "") + if (tabs[i].text != "") { ms.width += get_theme_constant("hseparation"); + } } - ms.width += Math::ceil(font->get_string_size(tabs[i].xl_text).width); + ms.width += Math::ceil(tabs[i].text_buf->get_size().x); + ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin); - if (tabs[i].disabled) + if (tabs[i].disabled) { ms.width += tab_disabled->get_minimum_size().width; - else if (current == i) - ms.width += tab_fg->get_minimum_size().width; - else - ms.width += tab_bg->get_minimum_size().width; + } else if (current == i) { + ms.width += tab_selected->get_minimum_size().width; + } else { + ms.width += tab_unselected->get_minimum_size().width; + } if (tabs[i].right_button.is_valid()) { Ref<Texture2D> rb = tabs[i].right_button; Size2 bms = rb->get_size(); bms.width += get_theme_constant("hseparation"); ms.width += bms.width; - ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height); + ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); } if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { @@ -75,7 +79,7 @@ Size2 Tabs::get_minimum_size() const { Size2 bms = cb->get_size(); bms.width += get_theme_constant("hseparation"); ms.width += bms.width; - ms.height = MAX(bms.height + tab_bg->get_minimum_size().height, ms.height); + ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height); } } @@ -84,25 +88,29 @@ Size2 Tabs::get_minimum_size() const { } void Tabs::_gui_input(const Ref<InputEvent> &p_event) { - Ref<InputEventMouseMotion> mm = p_event; if (mm.is_valid()) { - Point2 pos = mm->get_position(); highlight_arrow = -1; if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); - - if (pos.x > limit + decr->get_width()) { - highlight_arrow = 1; - } else if (pos.x > limit) { - highlight_arrow = 0; + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + highlight_arrow = 1; + } else if (pos.x < incr->get_width() + decr->get_width()) { + highlight_arrow = 0; + } + } else { + int limit = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit + decr->get_width()) { + highlight_arrow = 1; + } else if (pos.x > limit) { + highlight_arrow = 0; + } } } @@ -114,9 +122,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventMouseButton> mb = p_event; if (mb.is_valid()) { - if (mb->is_pressed() && mb->get_button_index() == BUTTON_WHEEL_UP && !mb->get_command()) { - if (scrolling_enabled && buttons_visible) { if (offset > 0) { offset--; @@ -135,7 +141,6 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (rb_hover != -1) { //pressed emit_signal("right_button_pressed", rb_hover); @@ -146,10 +151,9 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == BUTTON_LEFT) { - if (cb_hover != -1) { //pressed - emit_signal("tab_close", cb_hover); + emit_signal("tab_closed", cb_hover); } cb_pressing = false; @@ -157,35 +161,47 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } if (mb->is_pressed() && (mb->get_button_index() == BUTTON_LEFT || (select_with_rmb && mb->get_button_index() == BUTTON_RIGHT))) { - // clicks Point2 pos(mb->get_position().x, mb->get_position().y); if (buttons_visible) { - Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); - int limit = get_size().width - incr->get_width() - decr->get_width(); - - if (pos.x > limit + decr->get_width()) { - if (missing_right) { - offset++; - update(); + if (is_layout_rtl()) { + if (pos.x < decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x < incr->get_width() + decr->get_width()) { + if (offset > 0) { + offset--; + update(); + } + return; } - return; - } else if (pos.x > limit) { - if (offset > 0) { - offset--; - update(); + } else { + int limit = get_size().width - incr->get_width() - decr->get_width(); + if (pos.x > limit + decr->get_width()) { + if (missing_right) { + offset++; + update(); + } + return; + } else if (pos.x > limit) { + if (offset > 0) { + offset--; + update(); + } + return; } - return; } } int found = -1; for (int i = offset; i < tabs.size(); i++) { - if (tabs[i].rb_rect.has_point(pos)) { rb_pressing = true; update(); @@ -198,7 +214,7 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { return; } - if (pos.x >= tabs[i].ofs_cache && pos.x < tabs[i].ofs_cache + tabs[i].size_cache) { + if (pos.x >= get_tab_rect(i).position.x && pos.x < get_tab_rect(i).position.x + tabs[i].size_cache) { if (!tabs[i].disabled) { found = i; } @@ -207,7 +223,6 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } if (found != -1) { - set_current_tab(found); emit_signal("tab_clicked", found); } @@ -215,14 +230,32 @@ void Tabs::_gui_input(const Ref<InputEvent> &p_event) { } } -void Tabs::_notification(int p_what) { +void Tabs::_shape(int p_tab) { + Ref<Font> font = get_theme_font("font"); + int font_size = get_theme_font_size("font_size"); + + tabs.write[p_tab].xl_text = tr(tabs[p_tab].text); + tabs.write[p_tab].text_buf->clear(); + if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { + tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + } else { + tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); + } - switch (p_what) { + tabs.write[p_tab].text_buf->add_string(tabs.write[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, (tabs[p_tab].language != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale()); +} +void Tabs::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + _update_cache(); + update(); + } break; case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < tabs.size(); ++i) { - tabs.write[i].xl_text = tr(tabs[i].text); + _shape(i); } + _update_cache(); minimum_size_changed(); update(); } break; @@ -235,21 +268,24 @@ void Tabs::_notification(int p_what) { _update_cache(); RID ci = get_canvas_item(); - Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); - Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); + Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected"); + Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); - Color color_fg = get_theme_color("font_color_fg"); - Color color_bg = get_theme_color("font_color_bg"); - Color color_disabled = get_theme_color("font_color_disabled"); + Color font_selected_color = get_theme_color("font_selected_color"); + Color font_unselected_color = get_theme_color("font_unselected_color"); + Color font_disabled_color = get_theme_color("font_disabled_color"); Ref<Texture2D> close = get_theme_icon("close"); + Color font_outline_color = get_theme_color("font_outline_color"); + int outline_size = get_theme_constant("outline_size"); + + Vector2 size = get_size(); + bool rtl = is_layout_rtl(); int h = get_size().height; int w = 0; int mw = 0; for (int i = 0; i < tabs.size(); i++) { - tabs.write[i].ofs_cache = mw; mw += get_tab_width(i); } @@ -274,7 +310,6 @@ void Tabs::_notification(int p_what) { missing_right = false; for (int i = offset; i < tabs.size(); i++) { - tabs.write[i].ofs_cache = w; int lsize = tabs[i].size_cache; @@ -284,13 +319,13 @@ void Tabs::_notification(int p_what) { if (tabs[i].disabled) { sb = tab_disabled; - col = color_disabled; + col = font_disabled_color; } else if (i == current) { - sb = tab_fg; - col = color_fg; + sb = tab_selected; + col = font_selected_color; } else { - sb = tab_bg; - col = color_bg; + sb = tab_unselected; + col = font_unselected_color; } if (w + lsize > limit) { @@ -301,26 +336,46 @@ void Tabs::_notification(int p_what) { max_drawn_tab = i; } - Rect2 sb_rect = Rect2(w, 0, tabs[i].size_cache, h); + Rect2 sb_rect; + if (rtl) { + sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h); + } else { + sb_rect = Rect2(w, 0, tabs[i].size_cache, h); + } sb->draw(ci, sb_rect); - w += sb->get_margin(MARGIN_LEFT); + w += sb->get_margin(SIDE_LEFT); Size2i sb_ms = sb->get_minimum_size(); Ref<Texture2D> icon = tabs[i].icon; if (icon.is_valid()) { - - icon->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); - if (tabs[i].text != "") + if (rtl) { + icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } else { + icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2)); + } + if (tabs[i].text != "") { w += icon->get_width() + get_theme_constant("hseparation"); + } } - font->draw(ci, Point2i(w, sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - font->get_height()) / 2 + font->get_ascent()), tabs[i].xl_text, col, tabs[i].size_text); + if (rtl) { + Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); + if (outline_size > 0 && font_outline_color.a > 0) { + tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + } + tabs[i].text_buf->draw(ci, text_pos, col); + } else { + Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2); + if (outline_size > 0 && font_outline_color.a > 0) { + tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + } + tabs[i].text_buf->draw(ci, text_pos, col); + } w += tabs[i].size_text; if (tabs[i].right_button.is_valid()) { - Ref<StyleBox> style = get_theme_stylebox("button"); Ref<Texture2D> rb = tabs[i].right_button; @@ -328,23 +383,31 @@ void Tabs::_notification(int p_what) { Rect2 rb_rect; rb_rect.size = style->get_minimum_size() + rb->get_size(); - rb_rect.position.x = w; - rb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; + if (rtl) { + rb_rect.position.x = size.width - w - rb_rect.size.x; + } else { + rb_rect.position.x = w; + } + rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2; if (rb_hover == i) { - if (rb_pressing) + if (rb_pressing) { get_theme_stylebox("button_pressed")->draw(ci, rb_rect); - else + } else { style->draw(ci, rb_rect); + } } - rb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), rb_rect.position.y + style->get_margin(MARGIN_TOP))); + if (rtl) { + rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); + } w += rb->get_width(); tabs.write[i].rb_rect = rb_rect; } if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) { - Ref<StyleBox> style = get_theme_stylebox("button"); Ref<Texture2D> cb = close; @@ -352,37 +415,61 @@ void Tabs::_notification(int p_what) { Rect2 cb_rect; cb_rect.size = style->get_minimum_size() + cb->get_size(); - cb_rect.position.x = w; - cb_rect.position.y = sb->get_margin(MARGIN_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; + if (rtl) { + cb_rect.position.x = size.width - w - cb_rect.size.x; + } else { + cb_rect.position.x = w; + } + cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2; if (!tabs[i].disabled && cb_hover == i) { - if (cb_pressing) + if (cb_pressing) { get_theme_stylebox("button_pressed")->draw(ci, cb_rect); - else + } else { style->draw(ci, cb_rect); + } } - cb->draw(ci, Point2i(w + style->get_margin(MARGIN_LEFT), cb_rect.position.y + style->get_margin(MARGIN_TOP))); + if (rtl) { + cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } else { + cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP))); + } w += cb->get_width(); tabs.write[i].cb_rect = cb_rect; } - w += sb->get_margin(MARGIN_RIGHT); + w += sb->get_margin(SIDE_RIGHT); } if (offset > 0 || missing_right) { - int vofs = (get_size().height - incr->get_size().height) / 2; - if (offset > 0) - draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs)); - else - draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5)); + if (rtl) { + if (missing_right) { + draw_texture(highlight_arrow == 1 ? decr_hl : decr, Point2(0, vofs)); + } else { + draw_texture(decr, Point2(0, vofs), Color(1, 1, 1, 0.5)); + } - if (missing_right) - draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs)); - else - draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? incr_hl : incr, Point2(incr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(incr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } + } else { + if (offset > 0) { + draw_texture(highlight_arrow == 0 ? decr_hl : decr, Point2(limit, vofs)); + } else { + draw_texture(decr, Point2(limit, vofs), Color(1, 1, 1, 0.5)); + } + + if (missing_right) { + draw_texture(highlight_arrow == 1 ? incr_hl : incr, Point2(limit + decr->get_size().width, vofs)); + } else { + draw_texture(incr, Point2(limit + decr->get_size().width, vofs), Color(1, 1, 1, 0.5)); + } + } buttons_visible = true; } else { @@ -393,18 +480,18 @@ void Tabs::_notification(int p_what) { } int Tabs::get_tab_count() const { - return tabs.size(); } void Tabs::set_current_tab(int p_current) { - - if (current == p_current) return; + if (current == p_current) { + return; + } ERR_FAIL_INDEX(p_current, get_tab_count()); + previous = current; current = p_current; - _change_notify("current_tab"); _update_cache(); update(); @@ -412,10 +499,13 @@ void Tabs::set_current_tab(int p_current) { } int Tabs::get_current_tab() const { - return current; } +int Tabs::get_previous_tab() const { + return previous; +} + int Tabs::get_hovered_tab() const { return hover; } @@ -429,22 +519,75 @@ bool Tabs::get_offset_buttons_visible() const { } void Tabs::set_tab_title(int p_tab, const String &p_title) { - ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].text = p_title; tabs.write[p_tab].xl_text = tr(p_title); + _shape(p_tab); update(); minimum_size_changed(); } String Tabs::get_tab_title(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); return tabs[p_tab].text; } -void Tabs::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { +void Tabs::set_tab_text_direction(int p_tab, Control::TextDirection p_text_direction) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); + if (tabs[p_tab].text_direction != p_text_direction) { + tabs.write[p_tab].text_direction = p_text_direction; + _shape(p_tab); + update(); + } +} +Control::TextDirection Tabs::get_tab_text_direction(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), Control::TEXT_DIRECTION_INHERITED); + return tabs[p_tab].text_direction; +} + +void Tabs::clear_tab_opentype_features(int p_tab) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs.write[p_tab].opentype_features.clear(); + _shape(p_tab); + update(); +} + +void Tabs::set_tab_opentype_feature(int p_tab, const String &p_name, int p_value) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag) || (int)tabs[p_tab].opentype_features[tag] != p_value) { + tabs.write[p_tab].opentype_features[tag] = p_value; + _shape(p_tab); + update(); + } +} + +int Tabs::get_tab_opentype_feature(int p_tab, const String &p_name) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), -1); + int32_t tag = TS->name_to_tag(p_name); + if (!tabs[p_tab].opentype_features.has(tag)) { + return -1; + } + return tabs[p_tab].opentype_features[tag]; +} + +void Tabs::set_tab_language(int p_tab, const String &p_language) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + if (tabs[p_tab].language != p_language) { + tabs.write[p_tab].language = p_language; + _shape(p_tab); + update(); + } +} + +String Tabs::get_tab_language(int p_tab) const { + ERR_FAIL_INDEX_V(p_tab, tabs.size(), ""); + return tabs[p_tab].language; +} + +void Tabs::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].icon = p_icon; update(); @@ -452,39 +595,35 @@ void Tabs::set_tab_icon(int p_tab, const Ref<Texture2D> &p_icon) { } Ref<Texture2D> Tabs::get_tab_icon(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>()); return tabs[p_tab].icon; } void Tabs::set_tab_disabled(int p_tab, bool p_disabled) { - ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].disabled = p_disabled; update(); } -bool Tabs::get_tab_disabled(int p_tab) const { +bool Tabs::get_tab_disabled(int p_tab) const { ERR_FAIL_INDEX_V(p_tab, tabs.size(), false); return tabs[p_tab].disabled; } void Tabs::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button) { - ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].right_button = p_right_button; _update_cache(); update(); minimum_size_changed(); } -Ref<Texture2D> Tabs::get_tab_right_button(int p_tab) const { +Ref<Texture2D> Tabs::get_tab_right_button(int p_tab) const { ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>()); return tabs[p_tab].right_button; } void Tabs::_update_hover() { - if (!is_inside_tree()) { return; } @@ -494,7 +633,6 @@ void Tabs::_update_hover() { int hover_now = -1; int hover_buttons = -1; for (int i = offset; i < tabs.size(); i++) { - Rect2 rect = get_tab_rect(i); if (rect.has_point(pos)) { hover_now = i; @@ -513,7 +651,7 @@ void Tabs::_update_hover() { } if (hover != hover_now) { hover = hover_now; - emit_signal("tab_hover", hover); + emit_signal("tab_hovered", hover); } if (hover_buttons == -1) { // no hover @@ -524,9 +662,8 @@ void Tabs::_update_hover() { void Tabs::_update_cache() { Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); - Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); - Ref<Font> font = get_theme_font("font"); + Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected"); + Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected"); Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); int limit = get_size().width - incr->get_width() - decr->get_width(); @@ -538,7 +675,8 @@ void Tabs::_update_cache() { for (int i = 0; i < tabs.size(); i++) { tabs.write[i].ofs_cache = mw; tabs.write[i].size_cache = get_tab_width(i); - tabs.write[i].size_text = Math::ceil(font->get_string_size(tabs[i].xl_text).width); + tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x); + tabs.write[i].text_buf->set_width(-1); mw += tabs[i].size_cache; if (tabs[i].size_cache <= min_width || i == current) { size_fixed += tabs[i].size_cache; @@ -551,20 +689,19 @@ void Tabs::_update_cache() { m_width = MAX((limit - size_fixed) / count_resize, min_width); } for (int i = offset; i < tabs.size(); i++) { - Ref<StyleBox> sb; if (tabs[i].disabled) { sb = tab_disabled; } else if (i == current) { - sb = tab_fg; + sb = tab_selected; } else { - sb = tab_bg; + sb = tab_unselected; } int lsize = tabs[i].size_cache; int slen = tabs[i].size_text; if (min_width > 0 && mw > limit && i != current) { if (lsize > m_width) { - slen = m_width - (sb->get_margin(MARGIN_LEFT) + sb->get_margin(MARGIN_RIGHT)); + slen = m_width - (sb->get_margin(SIDE_LEFT) + sb->get_margin(SIDE_RIGHT)); if (tabs[i].icon.is_valid()) { slen -= tabs[i].icon->get_width(); slen -= get_theme_constant("hseparation"); @@ -581,12 +718,12 @@ void Tabs::_update_cache() { tabs.write[i].ofs_cache = w; tabs.write[i].size_cache = lsize; tabs.write[i].size_text = slen; + tabs.write[i].text_buf->set_width(slen); w += lsize; } } void Tabs::_on_mouse_exited() { - rb_hover = -1; cb_hover = -1; hover = -1; @@ -595,10 +732,12 @@ void Tabs::_on_mouse_exited() { } void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { - Tab t; t.text = p_str; t.xl_text = tr(p_str); + t.text_buf.instance(); + t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); + t.text_buf->add_string(t.xl_text, get_theme_font("font"), get_theme_font_size("font_size"), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); t.icon = p_icon; t.disabled = false; t.ofs_cache = 0; @@ -614,38 +753,43 @@ void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { void Tabs::clear_tabs() { tabs.clear(); current = 0; + previous = 0; call_deferred("_update_hover"); update(); } void Tabs::remove_tab(int p_idx) { - ERR_FAIL_INDEX(p_idx, tabs.size()); tabs.remove(p_idx); - if (current >= p_idx) + if (current >= p_idx) { current--; + } _update_cache(); call_deferred("_update_hover"); update(); minimum_size_changed(); - if (current < 0) + if (current < 0) { current = 0; - if (current >= tabs.size()) + previous = 0; + } + if (current >= tabs.size()) { current = tabs.size() - 1; + } _ensure_no_over_offset(); } Variant Tabs::get_drag_data(const Point2 &p_point) { - - if (!drag_to_rearrange_enabled) + if (!drag_to_rearrange_enabled) { return Variant(); + } int tab_over = get_tab_idx_at_point(p_point); - if (tab_over < 0) + if (tab_over < 0) { return Variant(); + } HBoxContainer *drag_preview = memnew(HBoxContainer); @@ -671,16 +815,16 @@ Variant Tabs::get_drag_data(const Point2 &p_point) { } bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - - if (!drag_to_rearrange_enabled) + if (!drag_to_rearrange_enabled) { return false; + } Dictionary d = p_data; - if (!d.has("type")) + if (!d.has("type")) { return false; + } if (String(d["type"]) == "tab_element") { - NodePath from_path = d["from_path"]; NodePath to_path = get_path(); if (from_path == to_path) { @@ -698,24 +842,25 @@ bool Tabs::can_drop_data(const Point2 &p_point, const Variant &p_data) const { } void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { - - if (!drag_to_rearrange_enabled) + if (!drag_to_rearrange_enabled) { return; + } int hover_now = get_tab_idx_at_point(p_point); Dictionary d = p_data; - if (!d.has("type")) + if (!d.has("type")) { return; + } if (String(d["type"]) == "tab_element") { - int tab_from_id = d["tab_element"]; NodePath from_path = d["from_path"]; NodePath to_path = get_path(); if (from_path == to_path) { - if (hover_now < 0) + if (hover_now < 0) { hover_now = get_tab_count() - 1; + } move_tab(tab_from_id, hover_now); emit_signal("reposition_active_tab_request", hover_now); set_current_tab(hover_now); @@ -724,11 +869,13 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { Node *from_node = get_node(from_path); Tabs *from_tabs = Object::cast_to<Tabs>(from_node); if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { - if (tab_from_id >= from_tabs->get_tab_count()) + if (tab_from_id >= from_tabs->get_tab_count()) { return; + } Tab moving_tab = from_tabs->tabs[tab_from_id]; - if (hover_now < 0) + if (hover_now < 0) { hover_now = get_tab_count(); + } tabs.insert(hover_now, moving_tab); from_tabs->remove_tab(tab_from_id); set_current_tab(hover_now); @@ -741,10 +888,8 @@ void Tabs::drop_data(const Point2 &p_point, const Variant &p_data) { } int Tabs::get_tab_idx_at_point(const Point2 &p_point) const { - int hover_now = -1; for (int i = offset; i < tabs.size(); i++) { - Rect2 rect = get_tab_rect(i); if (rect.has_point(p_point)) { hover_now = i; @@ -755,21 +900,19 @@ int Tabs::get_tab_idx_at_point(const Point2 &p_point) const { } void Tabs::set_tab_align(TabAlign p_align) { - ERR_FAIL_INDEX(p_align, ALIGN_MAX); tab_align = p_align; update(); } Tabs::TabAlign Tabs::get_tab_align() const { - return tab_align; } void Tabs::move_tab(int from, int to) { - - if (from == to) + if (from == to) { return; + } ERR_FAIL_INDEX(from, tabs.size()); ERR_FAIL_INDEX(to, tabs.size()); @@ -783,31 +926,31 @@ void Tabs::move_tab(int from, int to) { } int Tabs::get_tab_width(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, tabs.size(), 0); - Ref<StyleBox> tab_bg = get_theme_stylebox("tab_bg"); - Ref<StyleBox> tab_fg = get_theme_stylebox("tab_fg"); + Ref<StyleBox> tab_unselected = get_theme_stylebox("tab_unselected"); + Ref<StyleBox> tab_selected = get_theme_stylebox("tab_selected"); Ref<StyleBox> tab_disabled = get_theme_stylebox("tab_disabled"); - Ref<Font> font = get_theme_font("font"); int x = 0; Ref<Texture2D> tex = tabs[p_idx].icon; if (tex.is_valid()) { x += tex->get_width(); - if (tabs[p_idx].text != "") + if (tabs[p_idx].text != "") { x += get_theme_constant("hseparation"); + } } - x += Math::ceil(font->get_string_size(tabs[p_idx].xl_text).width); + x += Math::ceil(tabs[p_idx].text_buf->get_size().x); - if (tabs[p_idx].disabled) + if (tabs[p_idx].disabled) { x += tab_disabled->get_minimum_size().width; - else if (current == p_idx) - x += tab_fg->get_minimum_size().width; - else - x += tab_bg->get_minimum_size().width; + } else if (current == p_idx) { + x += tab_selected->get_minimum_size().width; + } else { + x += tab_unselected->get_minimum_size().width; + } if (tabs[p_idx].right_button.is_valid()) { Ref<Texture2D> rb = tabs[p_idx].right_button; @@ -825,9 +968,9 @@ int Tabs::get_tab_width(int p_idx) const { } void Tabs::_ensure_no_over_offset() { - - if (!is_inside_tree()) + if (!is_inside_tree()) { return; + } Ref<Texture2D> incr = get_theme_icon("increment"); Ref<Texture2D> decr = get_theme_icon("decrement"); @@ -835,10 +978,8 @@ void Tabs::_ensure_no_over_offset() { int limit = get_size().width - incr->get_width() - decr->get_width(); while (offset > 0) { - int total_w = 0; for (int i = offset - 1; i < tabs.size(); i++) { - total_w += tabs[i].size_cache; } @@ -852,11 +993,13 @@ void Tabs::_ensure_no_over_offset() { } void Tabs::ensure_tab_visible(int p_idx) { - - if (!is_inside_tree()) + if (!is_inside_tree()) { return; + } - if (tabs.size() == 0) return; + if (tabs.size() == 0) { + return; + } ERR_FAIL_INDEX(p_idx, tabs.size()); if (p_idx == offset) { @@ -884,20 +1027,21 @@ void Tabs::ensure_tab_visible(int p_idx) { } Rect2 Tabs::get_tab_rect(int p_tab) const { - ERR_FAIL_INDEX_V(p_tab, tabs.size(), Rect2()); - return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); + if (is_layout_rtl()) { + return Rect2(get_size().width - tabs[p_tab].ofs_cache - tabs[p_tab].size_cache, 0, tabs[p_tab].size_cache, get_size().height); + } else { + return Rect2(tabs[p_tab].ofs_cache, 0, tabs[p_tab].size_cache, get_size().height); + } } void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) { - ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX); cb_displaypolicy = p_policy; update(); } Tabs::CloseButtonDisplayPolicy Tabs::get_tab_close_display_policy() const { - return cb_displaypolicy; } @@ -920,6 +1064,7 @@ void Tabs::set_drag_to_rearrange_enabled(bool p_enabled) { bool Tabs::get_drag_to_rearrange_enabled() const { return drag_to_rearrange_enabled; } + void Tabs::set_tabs_rearrange_group(int p_group_id) { tabs_rearrange_group = p_group_id; } @@ -937,14 +1082,21 @@ bool Tabs::get_select_with_rmb() const { } void Tabs::_bind_methods() { - ClassDB::bind_method(D_METHOD("_gui_input"), &Tabs::_gui_input); ClassDB::bind_method(D_METHOD("_update_hover"), &Tabs::_update_hover); ClassDB::bind_method(D_METHOD("get_tab_count"), &Tabs::get_tab_count); ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &Tabs::set_current_tab); ClassDB::bind_method(D_METHOD("get_current_tab"), &Tabs::get_current_tab); + ClassDB::bind_method(D_METHOD("get_previous_tab"), &Tabs::get_previous_tab); ClassDB::bind_method(D_METHOD("set_tab_title", "tab_idx", "title"), &Tabs::set_tab_title); ClassDB::bind_method(D_METHOD("get_tab_title", "tab_idx"), &Tabs::get_tab_title); + ClassDB::bind_method(D_METHOD("set_tab_text_direction", "tab_idx", "direction"), &Tabs::set_tab_text_direction); + ClassDB::bind_method(D_METHOD("get_tab_text_direction", "tab_idx"), &Tabs::get_tab_text_direction); + ClassDB::bind_method(D_METHOD("set_tab_opentype_feature", "tab_idx", "tag", "values"), &Tabs::set_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("get_tab_opentype_feature", "tab_idx", "tag"), &Tabs::get_tab_opentype_feature); + ClassDB::bind_method(D_METHOD("clear_tab_opentype_features", "tab_idx"), &Tabs::clear_tab_opentype_features); + ClassDB::bind_method(D_METHOD("set_tab_language", "tab_idx", "language"), &Tabs::set_tab_language); + ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &Tabs::get_tab_language); ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &Tabs::set_tab_icon); ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &Tabs::get_tab_icon); ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &Tabs::set_tab_disabled); @@ -972,8 +1124,8 @@ void Tabs::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("right_button_pressed", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_close", PropertyInfo(Variant::INT, "tab"))); - ADD_SIGNAL(MethodInfo("tab_hover", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_closed", PropertyInfo(Variant::INT, "tab"))); + ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("reposition_active_tab_request", PropertyInfo(Variant::INT, "idx_to"))); ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab"))); @@ -995,27 +1147,5 @@ void Tabs::_bind_methods() { } Tabs::Tabs() { - - current = 0; - tab_align = ALIGN_CENTER; - rb_hover = -1; - rb_pressing = false; - highlight_arrow = -1; - - cb_hover = -1; - cb_pressing = false; - cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER; - offset = 0; - max_drawn_tab = 0; - - select_with_rmb = false; - - min_width = 0; - scrolling_enabled = true; - buttons_visible = false; - hover = -1; - drag_to_rearrange_enabled = false; - tabs_rearrange_group = -1; - connect("mouse_exited", callable_mp(this, &Tabs::_on_mouse_exited)); } |