summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/aspect_ratio_container.cpp18
-rw-r--r--scene/gui/aspect_ratio_container.h3
-rw-r--r--scene/gui/base_button.cpp78
-rw-r--r--scene/gui/base_button.h3
-rw-r--r--scene/gui/box_container.cpp27
-rw-r--r--scene/gui/box_container.h3
-rw-r--r--scene/gui/button.cpp7
-rw-r--r--scene/gui/center_container.cpp38
-rw-r--r--scene/gui/center_container.h3
-rw-r--r--scene/gui/check_box.cpp62
-rw-r--r--scene/gui/check_button.cpp93
-rw-r--r--scene/gui/check_button.h2
-rw-r--r--scene/gui/code_edit.cpp70
-rw-r--r--scene/gui/color_picker.cpp19
-rw-r--r--scene/gui/color_picker.h2
-rw-r--r--scene/gui/color_rect.cpp6
-rw-r--r--scene/gui/container.cpp38
-rw-r--r--scene/gui/container.h6
-rw-r--r--scene/gui/control.cpp504
-rw-r--r--scene/gui/control.h31
-rw-r--r--scene/gui/dialogs.cpp11
-rw-r--r--scene/gui/file_dialog.cpp55
-rw-r--r--scene/gui/file_dialog.h1
-rw-r--r--scene/gui/flow_container.cpp28
-rw-r--r--scene/gui/flow_container.h5
-rw-r--r--scene/gui/gradient_edit.cpp129
-rw-r--r--scene/gui/graph_edit.cpp122
-rw-r--r--scene/gui/graph_edit.h2
-rw-r--r--scene/gui/graph_node.cpp20
-rw-r--r--scene/gui/graph_node.h3
-rw-r--r--scene/gui/grid_container.cpp3
-rw-r--r--scene/gui/item_list.cpp732
-rw-r--r--scene/gui/item_list.h2
-rw-r--r--scene/gui/label.cpp475
-rw-r--r--scene/gui/label.h1
-rw-r--r--scene/gui/line_edit.cpp32
-rw-r--r--scene/gui/line_edit.h2
-rw-r--r--scene/gui/link_button.cpp10
-rw-r--r--scene/gui/link_button.h3
-rw-r--r--scene/gui/margin_container.cpp19
-rw-r--r--scene/gui/margin_container.h3
-rw-r--r--scene/gui/menu_button.cpp18
-rw-r--r--scene/gui/menu_button.h2
-rw-r--r--scene/gui/nine_patch_rect.cpp24
-rw-r--r--scene/gui/option_button.cpp55
-rw-r--r--scene/gui/option_button.h2
-rw-r--r--scene/gui/panel.cpp31
-rw-r--r--scene/gui/panel.h15
-rw-r--r--scene/gui/panel_container.cpp86
-rw-r--r--scene/gui/panel_container.h3
-rw-r--r--scene/gui/popup.cpp80
-rw-r--r--scene/gui/popup.h6
-rw-r--r--scene/gui/popup_menu.cpp251
-rw-r--r--scene/gui/popup_menu.h3
-rw-r--r--scene/gui/progress_bar.cpp55
-rw-r--r--scene/gui/range.cpp5
-rw-r--r--scene/gui/range.h4
-rw-r--r--scene/gui/reference_rect.cpp16
-rw-r--r--scene/gui/rich_text_label.cpp320
-rw-r--r--scene/gui/rich_text_label.h26
-rw-r--r--scene/gui/scroll_bar.cpp302
-rw-r--r--scene/gui/scroll_container.cpp179
-rw-r--r--scene/gui/separator.cpp1
-rw-r--r--scene/gui/slider.cpp8
-rw-r--r--scene/gui/spin_box.cpp69
-rw-r--r--scene/gui/spin_box.h2
-rw-r--r--scene/gui/split_container.cpp28
-rw-r--r--scene/gui/split_container.h3
-rw-r--r--scene/gui/subviewport_container.cpp101
-rw-r--r--scene/gui/subviewport_container.h4
-rw-r--r--scene/gui/tab_bar.cpp683
-rw-r--r--scene/gui/tab_bar.h27
-rw-r--r--scene/gui/tab_container.cpp1101
-rw-r--r--scene/gui/tab_container.h63
-rw-r--r--scene/gui/text_edit.cpp174
-rw-r--r--scene/gui/text_edit.h5
-rw-r--r--scene/gui/texture_button.cpp105
-rw-r--r--scene/gui/texture_button.h8
-rw-r--r--scene/gui/texture_rect.cpp151
-rw-r--r--scene/gui/tree.cpp386
-rw-r--r--scene/gui/tree.h13
-rw-r--r--scene/gui/video_stream_player.cpp49
82 files changed, 4134 insertions, 3001 deletions
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index 181d1bf33b..b59eda465e 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -70,6 +70,24 @@ void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vert
queue_sort();
}
+Vector<int> AspectRatioContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> AspectRatioContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void AspectRatioContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h
index 4a168bad14..6740e2f329 100644
--- a/scene/gui/aspect_ratio_container.h
+++ b/scene/gui/aspect_ratio_container.h
@@ -72,6 +72,9 @@ public:
void set_alignment_vertical(AlignmentMode p_alignment_vertical);
AlignmentMode get_alignment_vertical() const { return alignment_vertical; }
+
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
};
VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 5f937acb8d..ab86face7e 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -81,42 +81,50 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) {
}
void BaseButton::_notification(int p_what) {
- if (p_what == NOTIFICATION_MOUSE_ENTER) {
- status.hovering = true;
- update();
- }
+ switch (p_what) {
+ case NOTIFICATION_MOUSE_ENTER: {
+ status.hovering = true;
+ update();
+ } break;
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- status.hovering = false;
- update();
- }
- if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) {
- if (status.press_attempt) {
- status.press_attempt = false;
+ case NOTIFICATION_MOUSE_EXIT: {
+ status.hovering = false;
update();
- }
- }
+ } break;
- if (p_what == NOTIFICATION_FOCUS_ENTER) {
- update();
- }
+ case NOTIFICATION_DRAG_BEGIN:
+ case NOTIFICATION_SCROLL_BEGIN: {
+ if (status.press_attempt) {
+ status.press_attempt = false;
+ update();
+ }
+ } break;
- if (p_what == NOTIFICATION_FOCUS_EXIT) {
- if (status.press_attempt) {
- status.press_attempt = false;
+ case NOTIFICATION_FOCUS_ENTER: {
update();
- } else if (status.hovering) {
- update();
- }
- }
+ } break;
- if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) {
- if (!toggle_mode) {
- status.pressed = false;
- }
- status.hovering = false;
- status.press_attempt = false;
- status.pressing_inside = false;
+ case NOTIFICATION_FOCUS_EXIT: {
+ if (status.press_attempt) {
+ status.press_attempt = false;
+ update();
+ } else if (status.hovering) {
+ update();
+ }
+ } break;
+
+ case NOTIFICATION_VISIBILITY_CHANGED:
+ case NOTIFICATION_EXIT_TREE: {
+ if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) {
+ break;
+ }
+ if (!toggle_mode) {
+ status.pressed = false;
+ }
+ status.hovering = false;
+ status.press_attempt = false;
+ status.pressing_inside = false;
+ } break;
}
}
@@ -341,7 +349,7 @@ Ref<Shortcut> BaseButton::get_shortcut() const {
void BaseButton::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!_is_focus_owner_in_shorcut_context()) {
+ if (!_is_focus_owner_in_shortcut_context()) {
return;
}
@@ -396,14 +404,14 @@ Node *BaseButton::get_shortcut_context() const {
return ctx_node;
}
-bool BaseButton::_is_focus_owner_in_shorcut_context() const {
+bool BaseButton::_is_focus_owner_in_shortcut_context() const {
if (shortcut_context == ObjectID()) {
// No context, therefore global - always "in" context.
return true;
}
Node *ctx_node = get_shortcut_context();
- Control *vp_focus = get_focus_owner();
+ Control *vp_focus = get_viewport() ? get_viewport()->gui_get_focus_owner() : nullptr;
// If the context is valid and the viewport focus is valid, check if the context is the focus or is a parent of it.
return ctx_node && vp_focus && (ctx_node == vp_focus || ctx_node->is_ancestor_of(vp_focus));
@@ -444,6 +452,7 @@ void BaseButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("button_up"));
ADD_SIGNAL(MethodInfo("button_down"));
ADD_SIGNAL(MethodInfo("toggled", PropertyInfo(Variant::BOOL, "button_pressed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled");
@@ -503,7 +512,8 @@ BaseButton *ButtonGroup::get_pressed_button() {
void ButtonGroup::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_pressed_button"), &ButtonGroup::get_pressed_button);
ClassDB::bind_method(D_METHOD("get_buttons"), &ButtonGroup::_get_buttons);
- ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button")));
+
+ ADD_SIGNAL(MethodInfo("pressed", PropertyInfo(Variant::OBJECT, "button", PROPERTY_HINT_RESOURCE_TYPE, "BaseButton")));
}
ButtonGroup::ButtonGroup() {
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 0bcad4fc0e..a2b6ee0845 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -31,6 +31,7 @@
#ifndef BASE_BUTTON_H
#define BASE_BUTTON_H
+#include "core/input/shortcut.h"
#include "scene/gui/control.h"
class ButtonGroup;
@@ -79,7 +80,7 @@ protected:
virtual void unhandled_key_input(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
- bool _is_focus_owner_in_shorcut_context() const;
+ bool _is_focus_owner_in_shortcut_context() const;
GDVIRTUAL0(_pressed)
GDVIRTUAL1(_toggled, bool)
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index 9827bd0cef..251648da69 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "box_container.h"
+
#include "label.h"
#include "margin_container.h"
@@ -294,9 +295,11 @@ void BoxContainer::_notification(int p_what) {
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
@@ -331,6 +334,30 @@ Control *BoxContainer::add_spacer(bool p_begin) {
return c;
}
+Vector<int> BoxContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> BoxContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
BoxContainer::BoxContainer(bool p_vertical) {
vertical = p_vertical;
}
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index 68d55e1aaf..3043c3ea45 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -62,6 +62,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
BoxContainer(bool p_vertical = false);
};
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 3ed1b873af..29a0681f9c 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -78,6 +78,7 @@ void Button::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
update();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED: {
xl_text = atr(text);
_shape();
@@ -85,12 +86,14 @@ void Button::_notification(int p_what) {
update_minimum_size();
update();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
_shape();
update_minimum_size();
update();
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2 size = get_size();
@@ -290,7 +293,7 @@ void Button::_notification(int p_what) {
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
text_buf->set_width(clip_text ? text_clip : -1);
- int text_width = clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x;
+ int text_width = MAX(1, clip_text ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
if (_internal_margin[SIDE_LEFT] > 0) {
text_clip -= _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation"));
@@ -580,8 +583,8 @@ void Button::_bind_methods() {
Button::Button(const String &p_text) {
text_buf.instantiate();
text_buf->set_flags(TextServer::BREAK_MANDATORY);
-
set_mouse_filter(MOUSE_FILTER_STOP);
+
set_text(p_text);
}
diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp
index f3306783f3..33ec4006ae 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -69,22 +69,32 @@ bool CenterContainer::is_using_top_left() const {
return use_top_left;
}
+Vector<int> CenterContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
+
+Vector<int> CenterContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
+
void CenterContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- Size2 size = get_size();
- for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c) {
- continue;
- }
- if (c->is_set_as_top_level()) {
- continue;
- }
+ switch (p_what) {
+ case NOTIFICATION_SORT_CHILDREN: {
+ Size2 size = get_size();
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
- Size2 minsize = c->get_combined_minimum_size();
- Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor();
- fit_child_in_rect(c, Rect2(ofs, minsize));
- }
+ Size2 minsize = c->get_combined_minimum_size();
+ Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor();
+ fit_child_in_rect(c, Rect2(ofs, minsize));
+ }
+ } break;
}
}
diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h
index 16a10c8070..c35e0c4e29 100644
--- a/scene/gui/center_container.h
+++ b/scene/gui/center_container.h
@@ -48,6 +48,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
CenterContainer();
};
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index da2d4369d1..063a154bb2 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -84,34 +84,40 @@ Size2 CheckBox::get_minimum_size() const {
}
void CheckBox::_notification(int p_what) {
- if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || (p_what == NOTIFICATION_TRANSLATION_CHANGED))) {
- if (is_layout_rtl()) {
- _set_internal_margin(SIDE_LEFT, 0.f);
- _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
- } else {
- _set_internal_margin(SIDE_LEFT, get_icon_size().width);
- _set_internal_margin(SIDE_RIGHT, 0.f);
- }
- } else if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
-
- Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
- Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
-
- Vector2 ofs;
- if (is_layout_rtl()) {
- ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width;
- } else {
- ofs.x = sb->get_margin(SIDE_LEFT);
- }
- ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust"));
-
- if (is_pressed()) {
- on->draw(ci, ofs);
- } else {
- off->draw(ci, ofs);
- }
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ if (is_layout_rtl()) {
+ _set_internal_margin(SIDE_LEFT, 0.f);
+ _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
+ } else {
+ _set_internal_margin(SIDE_LEFT, get_icon_size().width);
+ _set_internal_margin(SIDE_RIGHT, 0.f);
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+
+ Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : ""));
+ Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : ""));
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
+
+ Vector2 ofs;
+ if (is_layout_rtl()) {
+ ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width;
+ } else {
+ ofs.x = sb->get_margin(SIDE_LEFT);
+ }
+ ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust"));
+
+ if (is_pressed()) {
+ on->draw(ci, ofs);
+ } else {
+ off->draw(ci, ofs);
+ }
+ } break;
}
}
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index afb23a540b..527b0061ac 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -61,53 +61,62 @@ Size2 CheckButton::get_minimum_size() const {
}
void CheckButton::_notification(int p_what) {
- if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED)) {
- if (is_layout_rtl()) {
- _set_internal_margin(SIDE_LEFT, get_icon_size().width);
- _set_internal_margin(SIDE_RIGHT, 0.f);
- } else {
- _set_internal_margin(SIDE_LEFT, 0.f);
- _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
- }
- } else if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- bool rtl = is_layout_rtl();
-
- Ref<Texture2D> on;
- if (rtl) {
- on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored");
- } else {
- on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
- }
- Ref<Texture2D> off;
- if (rtl) {
- off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored");
- } else {
- off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
- }
-
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
- Vector2 ofs;
- Size2 tex_size = get_icon_size();
-
- if (rtl) {
- ofs.x = sb->get_margin(SIDE_LEFT);
- } else {
- ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT));
- }
- ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust"));
-
- if (is_pressed()) {
- on->draw(ci, ofs);
- } else {
- off->draw(ci, ofs);
- }
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ if (is_layout_rtl()) {
+ _set_internal_margin(SIDE_LEFT, get_icon_size().width);
+ _set_internal_margin(SIDE_RIGHT, 0.f);
+ } else {
+ _set_internal_margin(SIDE_LEFT, 0.f);
+ _set_internal_margin(SIDE_RIGHT, get_icon_size().width);
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
+
+ Ref<Texture2D> on;
+ if (rtl) {
+ on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored");
+ } else {
+ on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on");
+ }
+ Ref<Texture2D> off;
+ if (rtl) {
+ off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored");
+ } else {
+ off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off");
+ }
+
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal"));
+ Vector2 ofs;
+ Size2 tex_size = get_icon_size();
+
+ if (rtl) {
+ ofs.x = sb->get_margin(SIDE_LEFT);
+ } else {
+ ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT));
+ }
+ ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust"));
+
+ if (is_pressed()) {
+ on->draw(ci, ofs);
+ } else {
+ off->draw(ci, ofs);
+ }
+ } break;
}
}
-CheckButton::CheckButton() {
+CheckButton::CheckButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
+
set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+
if (is_layout_rtl()) {
_set_internal_margin(SIDE_LEFT, get_icon_size().width);
} else {
diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h
index 9a72d04db2..7d4bb8bdfc 100644
--- a/scene/gui/check_button.h
+++ b/scene/gui/check_button.h
@@ -42,7 +42,7 @@ protected:
void _notification(int p_what);
public:
- CheckButton();
+ CheckButton(const String &p_text = String());
~CheckButton();
};
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 5511a1d910..3fa0cec302 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -34,14 +34,6 @@
#include "core/string/string_builder.h"
#include "core/string/ustring.h"
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return !is_symbol(c);
-}
-
void CodeEdit::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED:
@@ -82,6 +74,7 @@ void CodeEdit::_notification(int p_what) {
line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color"));
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
const Size2 size = get_size();
@@ -92,7 +85,7 @@ void CodeEdit::_notification(int p_what) {
if (line_length_guideline_columns.size() > 0) {
const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width();
const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0);
- const int char_size = Math::round(font->get_char_size('0', 0, font_size).width);
+ const float char_size = font->get_char_size('0', 0, font_size).width;
for (int i = 0; i < line_length_guideline_columns.size(); i++) {
const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll();
@@ -232,7 +225,7 @@ void CodeEdit::_notification(int p_what) {
int begin = 0;
int end = 0;
- if (line.find(String::chr(0xFFFF)) != -1) {
+ if (line.contains(String::chr(0xFFFF))) {
begin = font->get_string_size(line.substr(0, line.find(String::chr(0xFFFF))), font_size).x;
end = font->get_string_size(line.substr(0, line.rfind(String::chr(0xFFFF))), font_size).x;
}
@@ -571,6 +564,8 @@ Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
// Overridable actions
void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
bool had_selection = has_selection();
+ String selection_text = (had_selection ? get_selected_text() : "");
+
if (had_selection) {
begin_complex_operation();
delete_selection();
@@ -591,27 +586,38 @@ void CodeEdit::_handle_unicode_input_internal(const uint32_t p_unicode) {
if (auto_brace_completion_enabled) {
int cl = get_caret_line();
int cc = get_caret_column();
- int caret_move_offset = 1;
- int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
-
- if (has_string_delimiter(chr) && cc > 0 && _is_char(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
- insert_text_at_caret(chr);
- } else if (cc < get_line(cl).length() && _is_char(get_line(cl)[cc])) {
- insert_text_at_caret(chr);
- } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
- caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
- } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ if (had_selection) {
insert_text_at_caret(chr);
+
+ String close_key = get_auto_brace_completion_close_key(chr);
+ if (!close_key.is_empty()) {
+ insert_text_at_caret(selection_text + close_key);
+ set_caret_column(get_caret_column() - 1);
+ }
} else {
- insert_text_at_caret(chr);
+ int caret_move_offset = 1;
+
+ int post_brace_pair = cc < get_line(cl).length() ? _get_auto_brace_pair_close_at_pos(cl, cc) : -1;
+
+ if (has_string_delimiter(chr) && cc > 0 && !is_symbol(get_line(cl)[cc - 1]) && post_brace_pair == -1) {
+ insert_text_at_caret(chr);
+ } else if (cc < get_line(cl).length() && !is_symbol(get_line(cl)[cc])) {
+ insert_text_at_caret(chr);
+ } else if (post_brace_pair != -1 && auto_brace_completion_pairs[post_brace_pair].close_key[0] == chr[0]) {
+ caret_move_offset = auto_brace_completion_pairs[post_brace_pair].close_key.length();
+ } else if (is_in_comment(cl, cc) != -1 || (is_in_string(cl, cc) != -1 && has_string_delimiter(chr))) {
+ insert_text_at_caret(chr);
+ } else {
+ insert_text_at_caret(chr);
- int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
- if (pre_brace_pair != -1) {
- insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ int pre_brace_pair = _get_auto_brace_pair_open_at_pos(cl, cc + 1);
+ if (pre_brace_pair != -1) {
+ insert_text_at_caret(auto_brace_completion_pairs[pre_brace_pair].close_key);
+ }
}
+ set_caret_column(cc + caret_move_offset);
}
- set_caret_column(cc + caret_move_offset);
} else {
insert_text_at_caret(chr);
}
@@ -988,7 +994,7 @@ void CodeEdit::_new_line(bool p_split_current_line, bool p_above) {
}
/* Make sure this is the last char, trailing whitespace or comments are okay. */
- if (should_indent && (!_is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
+ if (should_indent && (!is_whitespace(c) && is_in_comment(cl, cc) == -1)) {
should_indent = false;
}
}
@@ -1804,7 +1810,7 @@ void CodeEdit::request_code_completion(bool p_force) {
String line = get_line(get_caret_line());
int ofs = CLAMP(get_caret_column(), 0, line.length());
- if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || _is_char(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
+ if (ofs > 0 && (is_in_string(get_caret_line(), ofs) != -1 || !is_symbol(line[ofs - 1]) || code_completion_prefixes.has(line[ofs - 1]))) {
emit_signal(SNAME("code_completion_requested"));
} else if (ofs > 1 && line[ofs - 1] == ' ' && code_completion_prefixes.has(line[ofs - 2])) {
emit_signal(SNAME("code_completion_requested"));
@@ -1913,7 +1919,7 @@ void CodeEdit::confirm_code_completion(bool p_replace) {
if (merge_text) {
for (; caret_col < line.length(); caret_col++) {
- if (!_is_char(line[caret_col])) {
+ if (is_symbol(line[caret_col])) {
break;
}
}
@@ -2549,7 +2555,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
region = E->value();
in_region = true;
for (int i = E->key() - 2; i >= 0; i--) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2568,7 +2574,7 @@ int CodeEdit::_is_in_delimiter(int p_line, int p_column, DelimiterType p_type) c
}
for (int i = end_col; i < line.length(); i++) {
- if (!_is_whitespace(line[i])) {
+ if (!is_whitespace(line[i])) {
return -1;
}
}
@@ -2784,11 +2790,11 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
while (ofs > 0 && line[ofs] == ' ') {
ofs--;
}
- prev_is_word = _is_char(line[ofs]);
+ prev_is_word = !is_symbol(line[ofs]);
/* Otherwise get current word and set cofs to the start. */
} else {
int start_cofs = cofs;
- while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || _is_char(line[cofs - 1]))) {
+ while (cofs > 0 && line[cofs - 1] > 32 && (line[cofs - 1] == '/' || !is_symbol(line[cofs - 1]))) {
cofs--;
}
string_to_complete = line.substr(cofs, start_cofs - cofs);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 36ea843d1e..9f32ac223c 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -33,11 +33,11 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "scene/main/window.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
#endif
-#include "scene/main/window.h"
List<Color> ColorPicker::preset_cache;
@@ -45,7 +45,6 @@ void ColorPicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
_update_color();
-
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (preset_cache.is_empty()) {
@@ -1305,7 +1304,7 @@ void ColorPickerButton::pressed() {
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
- popup->set_as_minsize();
+ popup->reset_size();
picker->_update_presets();
Rect2i usable_rect = popup->get_usable_parent_rect();
@@ -1347,17 +1346,18 @@ void ColorPickerButton::_notification(int p_what) {
draw_texture(Control::get_theme_icon(SNAME("overbright_indicator"), SNAME("ColorPicker")), normal->get_offset());
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
if (popup) {
popup->hide();
}
} break;
- }
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (popup && !is_visible_in_tree()) {
- popup->hide();
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (popup && !is_visible_in_tree()) {
+ popup->hide();
+ }
+ } break;
}
}
@@ -1429,7 +1429,8 @@ void ColorPickerButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha");
}
-ColorPickerButton::ColorPickerButton() {
+ColorPickerButton::ColorPickerButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
}
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index d6067b1cf4..6f3e16009c 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -231,7 +231,7 @@ public:
ColorPicker *get_picker();
PopupPanel *get_popup();
- ColorPickerButton();
+ ColorPickerButton(const String &p_text = String());
};
VARIANT_ENUM_CAST(ColorPicker::PickerShapeType);
diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp
index dbac1fc78a..2955f74a0c 100644
--- a/scene/gui/color_rect.cpp
+++ b/scene/gui/color_rect.cpp
@@ -40,8 +40,10 @@ Color ColorRect::get_color() const {
}
void ColorRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- draw_rect(Rect2(Point2(), get_size()), color);
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ draw_rect(Rect2(Point2(), get_size()), color);
+ } break;
}
}
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index 7b213ec314..1dd88371ea 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "container.h"
+
#include "core/object/message_queue.h"
#include "scene/scene_string_names.h"
@@ -143,18 +144,46 @@ void Container::queue_sort() {
pending_sort = true;
}
+Vector<int> Container::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ if (GDVIRTUAL_CALL(_get_allowed_size_flags_horizontal, flags)) {
+ return flags;
+ }
+
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> Container::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ if (GDVIRTUAL_CALL(_get_allowed_size_flags_vertical, flags)) {
+ return flags;
+ }
+
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void Container::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
pending_sort = false;
queue_sort();
} break;
- case NOTIFICATION_RESIZED: {
- queue_sort();
- } break;
+
+ case NOTIFICATION_RESIZED:
case NOTIFICATION_THEME_CHANGED: {
queue_sort();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible_in_tree()) {
queue_sort();
@@ -177,6 +206,9 @@ void Container::_bind_methods() {
ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort);
ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect);
+ GDVIRTUAL_BIND(_get_allowed_size_flags_horizontal);
+ GDVIRTUAL_BIND(_get_allowed_size_flags_vertical);
+
BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN);
BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN);
diff --git a/scene/gui/container.h b/scene/gui/container.h
index 0e986f46ef..9ec4ad3200 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -46,6 +46,9 @@ protected:
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_horizontal)
+ GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_vertical)
+
void _notification(int p_what);
static void _bind_methods();
@@ -57,6 +60,9 @@ public:
void fit_child_in_rect(Control *p_child, const Rect2 &p_rect);
+ virtual Vector<int> get_allowed_size_flags_horizontal() const;
+ virtual Vector<int> get_allowed_size_flags_vertical() const;
+
TypedArray<String> get_configuration_warnings() const override;
Container();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 7ebc5c27f8..d2d1b5e9b7 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -47,7 +47,7 @@
#include "servers/text_server.h"
#ifdef TOOLS_ENABLED
-#include "editor/plugins/canvas_item_editor_plugin.h"
+#include "editor/plugins/control_editor_plugin.h"
#endif
#ifdef TOOLS_ENABLED
@@ -56,51 +56,71 @@ Dictionary Control::_edit_get_state() const {
s["rotation"] = get_rotation();
s["scale"] = get_scale();
s["pivot"] = get_pivot_offset();
+
Array anchors;
anchors.push_back(get_anchor(SIDE_LEFT));
anchors.push_back(get_anchor(SIDE_TOP));
anchors.push_back(get_anchor(SIDE_RIGHT));
anchors.push_back(get_anchor(SIDE_BOTTOM));
s["anchors"] = anchors;
+
Array offsets;
offsets.push_back(get_offset(SIDE_LEFT));
offsets.push_back(get_offset(SIDE_TOP));
offsets.push_back(get_offset(SIDE_RIGHT));
offsets.push_back(get_offset(SIDE_BOTTOM));
s["offsets"] = offsets;
+
+ s["layout_mode"] = _get_layout_mode();
+ s["anchors_layout_preset"] = _get_anchors_layout_preset();
+
return s;
}
void Control::_edit_set_state(const Dictionary &p_state) {
ERR_FAIL_COND((p_state.size() <= 0) ||
!p_state.has("rotation") || !p_state.has("scale") ||
- !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets"));
+ !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets") ||
+ !p_state.has("layout_mode") || !p_state.has("anchors_layout_preset"));
Dictionary state = p_state;
set_rotation(state["rotation"]);
set_scale(state["scale"]);
set_pivot_offset(state["pivot"]);
+
Array anchors = state["anchors"];
+
+ // If anchors are not in their default position, force the anchor layout mode in place of position.
+ LayoutMode _layout = (LayoutMode)(int)state["layout_mode"];
+ if (_layout == LayoutMode::LAYOUT_MODE_POSITION) {
+ bool anchors_mode = ((real_t)anchors[0] != 0.0 || (real_t)anchors[1] != 0.0 || (real_t)anchors[2] != 0.0 || (real_t)anchors[3] != 0.0);
+ if (anchors_mode) {
+ _layout = LayoutMode::LAYOUT_MODE_ANCHORS;
+ }
+ }
+
+ _set_layout_mode(_layout);
+ if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS) {
+ _set_anchors_layout_preset((int)state["anchors_layout_preset"]);
+ }
+
data.anchor[SIDE_LEFT] = anchors[0];
data.anchor[SIDE_TOP] = anchors[1];
data.anchor[SIDE_RIGHT] = anchors[2];
data.anchor[SIDE_BOTTOM] = anchors[3];
+
Array offsets = state["offsets"];
data.offset[SIDE_LEFT] = offsets[0];
data.offset[SIDE_TOP] = offsets[1];
data.offset[SIDE_RIGHT] = offsets[2];
data.offset[SIDE_BOTTOM] = offsets[3];
+
_size_changed();
}
void Control::_edit_set_position(const Point2 &p_position) {
-#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
- set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
-#else
- // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
- set_position(p_position);
-#endif
+ set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent));
};
Point2 Control::_edit_get_position() const {
@@ -116,15 +136,9 @@ Size2 Control::_edit_get_scale() const {
}
void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
-#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins.");
- set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
- set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled());
-#else
- // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED
- set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)));
- set_size(p_edit_rect.size.snapped(Vector2(1, 1)));
-#endif
+ set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
+ set_size(p_edit_rect.size.snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
}
Rect2 Control::_edit_get_rect() const {
@@ -176,9 +190,10 @@ String Control::properties_managed_by_container[] = {
"anchor_top",
"anchor_right",
"anchor_bottom",
- "rect_position",
- "rect_scale",
- "rect_size"
+ "position",
+ "rotation",
+ "scale",
+ "size"
};
void Control::accept_event() {
@@ -235,42 +250,41 @@ Transform2D Control::_get_internal_transform() const {
bool Control::_set(const StringName &p_name, const Variant &p_value) {
String name = p_name;
- // Prefixes "custom_*" are supported for compatibility with 3.x.
- if (!name.begins_with("theme_override") && !name.begins_with("custom")) {
+ if (!name.begins_with("theme_override")) {
return false;
}
if (p_value.get_type() == Variant::NIL || (p_value.get_type() == Variant::OBJECT && (Object *)p_value == nullptr)) {
- if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) {
+ if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
if (data.icon_override.has(dname)) {
data.icon_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
data.icon_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) {
+ } else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
if (data.style_override.has(dname)) {
data.style_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
data.style_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) {
+ } else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
if (data.font_override.has(dname)) {
data.font_override[dname]->disconnect("changed", callable_mp(this, &Control::_override_changed));
}
data.font_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) {
+ } else if (name.begins_with("theme_override_font_sizes/")) {
String dname = name.get_slicec('/', 1);
data.font_size_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) {
+ } else if (name.begins_with("theme_override_colors/")) {
String dname = name.get_slicec('/', 1);
data.color_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
- } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) {
+ } else if (name.begins_with("theme_override_constants/")) {
String dname = name.get_slicec('/', 1);
data.constant_override.erase(dname);
notification(NOTIFICATION_THEME_CHANGED);
@@ -279,22 +293,22 @@ bool Control::_set(const StringName &p_name, const Variant &p_value) {
}
} else {
- if (name.begins_with("theme_override_icons/") || name.begins_with("custom_icons/")) {
+ if (name.begins_with("theme_override_icons/")) {
String dname = name.get_slicec('/', 1);
add_theme_icon_override(dname, p_value);
- } else if (name.begins_with("theme_override_styles/") || name.begins_with("custom_styles/")) {
+ } else if (name.begins_with("theme_override_styles/")) {
String dname = name.get_slicec('/', 1);
add_theme_style_override(dname, p_value);
- } else if (name.begins_with("theme_override_fonts/") || name.begins_with("custom_fonts/")) {
+ } else if (name.begins_with("theme_override_fonts/")) {
String dname = name.get_slicec('/', 1);
add_theme_font_override(dname, p_value);
- } else if (name.begins_with("theme_override_font_sizes/") || name.begins_with("custom_font_sizes/")) {
+ } else if (name.begins_with("theme_override_font_sizes/")) {
String dname = name.get_slicec('/', 1);
add_theme_font_size_override(dname, p_value);
- } else if (name.begins_with("theme_override_colors/") || name.begins_with("custom_colors/")) {
+ } else if (name.begins_with("theme_override_colors/")) {
String dname = name.get_slicec('/', 1);
add_theme_color_override(dname, p_value);
- } else if (name.begins_with("theme_override_constants/") || name.begins_with("custom_constants/")) {
+ } else if (name.begins_with("theme_override_constants/")) {
String dname = name.get_slicec('/', 1);
add_theme_constant_override(dname, p_value);
} else {
@@ -430,6 +444,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const {
}
void Control::_validate_property(PropertyInfo &property) const {
+ // Update theme type variation options.
if (property.name == "theme_type_variation") {
List<StringName> names;
@@ -455,10 +470,99 @@ void Control::_validate_property(PropertyInfo &property) const {
property.hint_string = hint_string;
}
- if (!Object::cast_to<Container>(get_parent())) {
- return;
+
+ // Validate which positioning properties should be displayed depending on the parent and the layout mode.
+ Node *parent_node = get_parent_control();
+ if (!parent_node) {
+ // If there is no parent, display both anchor and container options.
+
+ // Set the layout mode to be disabled with the proper value.
+ if (property.name == "layout_mode") {
+ property.hint_string = "Position,Anchors,Container,Uncontrolled";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ }
+
+ // Use the layout mode to display or hide advanced anchoring properties.
+ bool use_custom_anchors = _get_anchors_layout_preset() == -1; // Custom "preset".
+ if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
+ } else if (Object::cast_to<Container>(parent_node)) {
+ // If the parent is a container, display only container-related properties.
+ if (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_") || property.name == "anchors_preset" ||
+ property.name == "position" || property.name == "rotation" || property.name == "scale" || property.name == "size" || property.name == "pivot_offset") {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+
+ } else if (property.name == "layout_mode") {
+ // Set the layout mode to be disabled with the proper value.
+ property.hint_string = "Position,Anchors,Container,Uncontrolled";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ } else if (property.name == "size_flags_horizontal" || property.name == "size_flags_vertical") {
+ // Filter allowed size flags based on the parent container configuration.
+ Container *parent_container = Object::cast_to<Container>(parent_node);
+ Vector<int> size_flags;
+ if (property.name == "size_flags_horizontal") {
+ size_flags = parent_container->get_allowed_size_flags_horizontal();
+ } else if (property.name == "size_flags_vertical") {
+ size_flags = parent_container->get_allowed_size_flags_vertical();
+ }
+
+ // Enforce the order of the options, regardless of what the container provided.
+ String hint_string;
+ if (size_flags.has(SIZE_FILL)) {
+ hint_string += "Fill:1";
+ }
+ if (size_flags.has(SIZE_EXPAND)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Expand:2";
+ }
+ if (size_flags.has(SIZE_SHRINK_CENTER)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Shrink Center:4";
+ }
+ if (size_flags.has(SIZE_SHRINK_END)) {
+ if (!hint_string.is_empty()) {
+ hint_string += ",";
+ }
+ hint_string += "Shrink End:8";
+ }
+
+ if (hint_string.is_empty()) {
+ property.hint_string = "";
+ property.usage |= PROPERTY_USAGE_READ_ONLY;
+ } else {
+ property.hint_string = hint_string;
+ }
+ }
+ } else {
+ // If the parent is NOT a container or not a control at all, display only anchoring-related properties.
+ if (property.name.begins_with("size_flags_")) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+
+ } else if (property.name == "layout_mode") {
+ // Set the layout mode to be enabled with proper options.
+ property.hint_string = "Position,Anchors";
+ }
+
+ // Use the layout mode to display or hide advanced anchoring properties.
+ bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS;
+ if (!use_anchors && property.name == "anchors_preset") {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
+ bool use_custom_anchors = use_anchors && _get_anchors_layout_preset() == -1; // Custom "preset".
+ if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) {
+ property.usage ^= PROPERTY_USAGE_EDITOR;
+ }
}
+
// Disable the property if it's managed by the parent container.
+ if (!Object::cast_to<Container>(parent_node)) {
+ return;
+ }
bool property_is_managed_by_container = false;
for (unsigned i = 0; i < properties_managed_by_container_count; i++) {
property_is_managed_by_container = properties_managed_by_container[i] == property.name;
@@ -586,17 +690,17 @@ void Control::_update_canvas_item_transform() {
void Control::_notification(int p_notification) {
switch (p_notification) {
- case NOTIFICATION_ENTER_TREE: {
- } break;
case NOTIFICATION_POST_ENTER_TREE: {
data.minimum_size_valid = false;
data.is_rtl_dirty = true;
_size_changed();
} break;
+
case NOTIFICATION_EXIT_TREE: {
release_focus();
get_viewport()->_gui_remove_control(this);
} break;
+
case NOTIFICATION_READY: {
#ifdef DEBUG_ENABLED
connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT);
@@ -659,6 +763,7 @@ void Control::_notification(int p_notification) {
viewport->connect("size_changed", callable_mp(this, &Control::_size_changed));
}
} break;
+
case NOTIFICATION_EXIT_CANVAS: {
if (data.parent_canvas_item) {
data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed));
@@ -679,8 +784,8 @@ void Control::_notification(int p_notification) {
data.parent_canvas_item = nullptr;
data.parent_window = nullptr;
data.is_rtl_dirty = true;
-
} break;
+
case NOTIFICATION_MOVED_IN_PARENT: {
// some parents need to know the order of the children to draw (like TabContainer)
// update if necessary
@@ -692,50 +797,52 @@ void Control::_notification(int p_notification) {
if (data.RI) {
get_viewport()->_gui_set_root_order_dirty();
}
-
} break;
+
case NOTIFICATION_RESIZED: {
emit_signal(SceneStringNames::get_singleton()->resized);
} break;
+
case NOTIFICATION_DRAW: {
_update_canvas_item_transform();
RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size()));
RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents);
- //emit_signal(SceneStringNames::get_singleton()->draw);
-
} break;
+
case NOTIFICATION_MOUSE_ENTER: {
emit_signal(SceneStringNames::get_singleton()->mouse_entered);
} break;
+
case NOTIFICATION_MOUSE_EXIT: {
emit_signal(SceneStringNames::get_singleton()->mouse_exited);
} break;
+
case NOTIFICATION_FOCUS_ENTER: {
emit_signal(SceneStringNames::get_singleton()->focus_entered);
update();
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
emit_signal(SceneStringNames::get_singleton()->focus_exited);
update();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
update();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
if (get_viewport() != nullptr) {
get_viewport()->_gui_hide_control(this);
}
-
- //remove key focus
-
} else {
data.minimum_size_valid = false;
_size_changed();
}
-
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
if (is_inside_tree()) {
@@ -1390,6 +1497,54 @@ void Control::_size_changed() {
}
}
+void Control::_set_layout_mode(LayoutMode p_mode) {
+ bool list_changed = false;
+
+ if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) {
+ if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)p_mode) {
+ list_changed = true;
+ }
+
+ set_meta("_edit_layout_mode", (int)p_mode);
+
+ if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) {
+ set_meta("_edit_use_custom_anchors", false);
+ set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE);
+ set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT);
+ }
+ } else {
+ if (has_meta("_edit_layout_mode")) {
+ remove_meta("_edit_layout_mode");
+ list_changed = true;
+ }
+ }
+
+ if (list_changed) {
+ notify_property_list_changed();
+ }
+}
+
+Control::LayoutMode Control::_get_layout_mode() const {
+ Node *parent_node = get_parent_control();
+ // In these modes the property is read-only.
+ if (!parent_node) {
+ return LayoutMode::LAYOUT_MODE_UNCONTROLLED;
+ } else if (Object::cast_to<Container>(parent_node)) {
+ return LayoutMode::LAYOUT_MODE_CONTAINER;
+ }
+
+ // If anchors are not in the top-left position, this is definitely in anchors mode.
+ if (_get_anchors_layout_preset() != (int)LayoutPreset::PRESET_TOP_LEFT) {
+ return LayoutMode::LAYOUT_MODE_ANCHORS;
+ }
+ // Otherwise check what was saved.
+ if (has_meta("_edit_layout_mode")) {
+ return (LayoutMode)(int)get_meta("_edit_layout_mode");
+ }
+ // Or fallback on default.
+ return LayoutMode::LAYOUT_MODE_POSITION;
+}
+
void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) {
ERR_FAIL_INDEX((int)p_side, 4);
@@ -1431,6 +1586,133 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos,
set_offset(p_side, p_pos);
}
+void Control::_set_anchors_layout_preset(int p_preset) {
+ bool list_changed = false;
+
+ if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)LayoutMode::LAYOUT_MODE_ANCHORS) {
+ list_changed = true;
+ set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS);
+ }
+
+ if (p_preset == -1) {
+ if (!has_meta("_edit_use_custom_anchors") || !(bool)get_meta("_edit_use_custom_anchors")) {
+ set_meta("_edit_use_custom_anchors", true);
+ notify_property_list_changed();
+ }
+ return; // Keep settings as is.
+ }
+
+ if (!has_meta("_edit_use_custom_anchors") || (bool)get_meta("_edit_use_custom_anchors")) {
+ list_changed = true;
+ set_meta("_edit_use_custom_anchors", false);
+ }
+
+ LayoutPreset preset = (LayoutPreset)p_preset;
+ // Set correct anchors.
+ set_anchors_preset(preset);
+
+ // Select correct preset mode.
+ switch (preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_KEEP_SIZE);
+ break;
+ case PRESET_LEFT_WIDE:
+ case PRESET_TOP_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE);
+ break;
+ }
+
+ // Select correct grow directions.
+ set_grow_direction_preset(preset);
+
+ if (list_changed) {
+ notify_property_list_changed();
+ }
+}
+
+int Control::_get_anchors_layout_preset() const {
+ // If the custom preset was selected by user, use it.
+ if (has_meta("_edit_use_custom_anchors") && (bool)get_meta("_edit_use_custom_anchors")) {
+ return -1;
+ }
+
+ // Check anchors to determine if the current state matches a preset, or not.
+
+ float left = get_anchor(SIDE_LEFT);
+ float right = get_anchor(SIDE_RIGHT);
+ float top = get_anchor(SIDE_TOP);
+ float bottom = get_anchor(SIDE_BOTTOM);
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_RIGHT;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_RIGHT;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER_LEFT;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER_RIGHT;
+ }
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_CENTER_TOP;
+ }
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_CENTER_BOTTOM;
+ }
+ if (left == 0.5 && right == 0.5 && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_CENTER;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_LEFT_WIDE;
+ }
+ if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_RIGHT_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) {
+ return (int)LayoutPreset::PRESET_TOP_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_BOTTOM_WIDE;
+ }
+
+ if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_VCENTER_WIDE;
+ }
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == 0.5 && bottom == 0.5) {
+ return (int)LayoutPreset::PRESET_HCENTER_WIDE;
+ }
+
+ if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) {
+ return (int)LayoutPreset::PRESET_WIDE;
+ }
+
+ // Does not match any preset, return "Custom".
+ return -1;
+}
+
void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) {
ERR_FAIL_INDEX((int)p_preset, 16);
@@ -1687,6 +1969,62 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset
set_offsets_preset(p_preset, p_resize_mode, p_margin);
}
+void Control::set_grow_direction_preset(LayoutPreset p_preset) {
+ // Select correct horizontal grow direction.
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_CENTER_LEFT:
+ case PRESET_LEFT_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_END);
+ break;
+ case PRESET_TOP_RIGHT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_RIGHT_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
+ break;
+ case PRESET_CENTER_TOP:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_CENTER:
+ case PRESET_TOP_WIDE:
+ case PRESET_BOTTOM_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
+ break;
+ }
+
+ // Select correct vertical grow direction.
+ switch (p_preset) {
+ case PRESET_TOP_LEFT:
+ case PRESET_TOP_RIGHT:
+ case PRESET_CENTER_TOP:
+ case PRESET_TOP_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_END);
+ break;
+
+ case PRESET_BOTTOM_LEFT:
+ case PRESET_BOTTOM_RIGHT:
+ case PRESET_CENTER_BOTTOM:
+ case PRESET_BOTTOM_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN);
+ break;
+
+ case PRESET_CENTER_LEFT:
+ case PRESET_CENTER_RIGHT:
+ case PRESET_CENTER:
+ case PRESET_LEFT_WIDE:
+ case PRESET_RIGHT_WIDE:
+ case PRESET_VCENTER_WIDE:
+ case PRESET_HCENTER_WIDE:
+ case PRESET_WIDE:
+ set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH);
+ break;
+ }
+}
+
real_t Control::get_anchor(Side p_side) const {
ERR_FAIL_INDEX_V(int(p_side), 4, 0.0);
@@ -2196,8 +2534,7 @@ void Control::release_focus() {
return;
}
- get_viewport()->_gui_remove_focus();
- update();
+ get_viewport()->gui_release_focus();
}
bool Control::is_top_level_control() const {
@@ -2600,11 +2937,6 @@ Control::MouseFilter Control::get_mouse_filter() const {
return data.mouse_filter;
}
-Control *Control::get_focus_owner() const {
- ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
- return get_viewport()->_gui_get_focus_owner();
-}
-
void Control::warp_mouse(const Point2 &p_to_pos) {
ERR_FAIL_COND(!is_inside_tree());
get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos));
@@ -2853,14 +3185,22 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event);
ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size);
ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size);
+
+ ClassDB::bind_method(D_METHOD("_set_layout_mode", "mode"), &Control::_set_layout_mode);
+ ClassDB::bind_method(D_METHOD("_get_layout_mode"), &Control::_get_layout_mode);
+ ClassDB::bind_method(D_METHOD("_set_anchors_layout_preset", "preset"), &Control::_set_anchors_layout_preset);
+ ClassDB::bind_method(D_METHOD("_get_anchors_layout_preset"), &Control::_get_anchors_layout_preset);
ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_offsets"), &Control::set_anchors_preset, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
ClassDB::bind_method(D_METHOD("set_anchors_and_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0));
+
ClassDB::bind_method(D_METHOD("_set_anchor", "side", "anchor"), &Control::_set_anchor);
ClassDB::bind_method(D_METHOD("set_anchor", "side", "anchor", "keep_offset", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true));
ClassDB::bind_method(D_METHOD("get_anchor", "side"), &Control::get_anchor);
ClassDB::bind_method(D_METHOD("set_offset", "side", "offset"), &Control::set_offset);
+ ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("set_anchor_and_offset", "side", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_offset, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin);
ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end);
ClassDB::bind_method(D_METHOD("set_position", "position", "keep_offsets"), &Control::set_position, DEFVAL(false));
@@ -2874,7 +3214,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset);
- ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset);
ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin);
ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end);
ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position);
@@ -2885,6 +3224,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size);
ClassDB::bind_method(D_METHOD("get_parent_area_size"), &Control::get_parent_area_size);
ClassDB::bind_method(D_METHOD("get_global_position"), &Control::get_global_position);
+ ClassDB::bind_method(D_METHOD("get_screen_position"), &Control::get_screen_position);
ClassDB::bind_method(D_METHOD("get_rect"), &Control::get_rect);
ClassDB::bind_method(D_METHOD("get_global_rect"), &Control::get_global_rect);
ClassDB::bind_method(D_METHOD("set_focus_mode", "mode"), &Control::set_focus_mode);
@@ -2894,7 +3234,6 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("release_focus"), &Control::release_focus);
ClassDB::bind_method(D_METHOD("find_prev_valid_focus"), &Control::find_prev_valid_focus);
ClassDB::bind_method(D_METHOD("find_next_valid_focus"), &Control::find_next_valid_focus);
- ClassDB::bind_method(D_METHOD("get_focus_owner"), &Control::get_focus_owner);
ClassDB::bind_method(D_METHOD("set_h_size_flags", "flags"), &Control::set_h_size_flags);
ClassDB::bind_method(D_METHOD("get_h_size_flags"), &Control::get_h_size_flags);
@@ -3003,38 +3342,53 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
- ADD_GROUP("Anchor", "anchor_");
+ ADD_GROUP("Layout", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_contents"), "set_clip_contents", "is_clipping_contents");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimum_size"), "set_custom_minimum_size", "get_custom_minimum_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode");
+ ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION);
+
+ const String anchors_presets_options = "Custom:-1,PresetWide:15,"
+ "PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2,"
+ "PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8,"
+ "PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14";
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset");
+ ADD_PROPERTY_DEFAULT("anchors_preset", -1);
+
+ ADD_SUBGROUP_INDENT("Anchor Points", "anchor_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM);
- ADD_GROUP("Offset", "offset_");
+ ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_LEFT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_TOP);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_RIGHT);
ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_BOTTOM);
- ADD_GROUP("Grow Direction", "grow_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
-
- ADD_GROUP("Layout Direction", "layout_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
+ ADD_SUBGROUP_INDENT("Grow Direction", "grow_", 1);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Left,Right,Both"), "set_h_grow_direction", "get_h_grow_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Top,Bottom,Both"), "set_v_grow_direction", "get_v_grow_direction");
+
+ ADD_SUBGROUP("Transform", "");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset"), "set_pivot_offset", "get_pivot_offset");
+
+ ADD_SUBGROUP("Container Sizing", "size_flags_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
ADD_GROUP("Auto Translate", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating");
- ADD_GROUP("Rect", "rect_");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale");
- ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents");
-
ADD_GROUP("Hint", "hint_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip");
@@ -3051,11 +3405,6 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
- ADD_GROUP("Size Flags", "size_flags_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio");
-
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation");
@@ -3114,6 +3463,7 @@ void Control::_bind_methods() {
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT);
BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE);
+ BIND_ENUM_CONSTANT(SIZE_SHRINK_BEGIN);
BIND_ENUM_CONSTANT(SIZE_FILL);
BIND_ENUM_CONSTANT(SIZE_EXPAND);
BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index bf79f790e7..becb50a118 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -31,12 +31,10 @@
#ifndef CONTROL_H
#define CONTROL_H
-#include "core/input/shortcut.h"
#include "core/math/transform_2d.h"
#include "core/object/gdvirtual.gen.inc"
#include "core/templates/rid.h"
#include "scene/main/canvas_item.h"
-#include "scene/main/node.h"
#include "scene/main/timer.h"
#include "scene/resources/theme.h"
@@ -67,12 +65,13 @@ public:
};
enum SizeFlags {
+ SIZE_SHRINK_BEGIN = 0,
SIZE_FILL = 1,
SIZE_EXPAND = 2,
- SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
- SIZE_SHRINK_CENTER = 4, //ignored by expand or fill
- SIZE_SHRINK_END = 8, //ignored by expand or fill
+ SIZE_SHRINK_CENTER = 4,
+ SIZE_SHRINK_END = 8,
+ SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL,
};
enum MouseFilter {
@@ -128,6 +127,13 @@ public:
PRESET_MODE_KEEP_SIZE
};
+ enum LayoutMode {
+ LAYOUT_MODE_POSITION,
+ LAYOUT_MODE_ANCHORS,
+ LAYOUT_MODE_CONTAINER,
+ LAYOUT_MODE_UNCONTROLLED,
+ };
+
enum LayoutDirection {
LAYOUT_DIRECTION_INHERITED,
LAYOUT_DIRECTION_LOCALE,
@@ -230,7 +236,7 @@ private:
} data;
- static constexpr unsigned properties_managed_by_container_count = 11;
+ static constexpr unsigned properties_managed_by_container_count = 12;
static String properties_managed_by_container[properties_managed_by_container_count];
void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest);
@@ -241,6 +247,12 @@ private:
void _set_global_position(const Point2 &p_point);
void _set_size(const Size2 &p_size);
+ void _set_layout_mode(LayoutMode p_mode);
+ LayoutMode _get_layout_mode() const;
+
+ void _set_anchors_layout_preset(int p_preset);
+ int _get_anchors_layout_preset() const;
+
void _theme_changed();
void _notify_theme_changed();
@@ -285,9 +297,10 @@ protected:
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
+ virtual void _validate_property(PropertyInfo &property) const override;
+
void _notification(int p_notification);
static void _bind_methods();
- virtual void _validate_property(PropertyInfo &property) const override;
//bind helpers
@@ -378,6 +391,7 @@ public:
void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true);
void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0);
+ void set_grow_direction_preset(LayoutPreset p_preset);
void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true);
real_t get_anchor(Side p_side) const;
@@ -462,8 +476,6 @@ public:
void set_focus_previous(const NodePath &p_prev);
NodePath get_focus_previous() const;
- Control *get_focus_owner() const;
-
void set_mouse_filter(MouseFilter p_filter);
MouseFilter get_mouse_filter() const;
@@ -565,6 +577,7 @@ VARIANT_ENUM_CAST(Control::LayoutPresetMode);
VARIANT_ENUM_CAST(Control::MouseFilter);
VARIANT_ENUM_CAST(Control::GrowDirection);
VARIANT_ENUM_CAST(Control::Anchor);
+VARIANT_ENUM_CAST(Control::LayoutMode);
VARIANT_ENUM_CAST(Control::LayoutDirection);
VARIANT_ENUM_CAST(Control::TextDirection);
VARIANT_ENUM_CAST(Control::StructuredTextParser);
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index 1cbe3adb3c..e3744eedca 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -33,12 +33,7 @@
#include "core/os/keyboard.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
-#include "line_edit.h"
-
-#ifdef TOOLS_ENABLED
-#include "editor/editor_node.h"
-#include "scene/main/window.h" // Only used to check for more modals when dimming the editor.
-#endif
+#include "scene/gui/line_edit.h"
// AcceptDialog
@@ -72,21 +67,25 @@ void AcceptDialog::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
bg->add_theme_style_override("panel", bg->get_theme_stylebox(SNAME("panel"), SNAME("AcceptDialog")));
} break;
+
case NOTIFICATION_EXIT_TREE: {
if (parent_visible) {
parent_visible->disconnect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused));
parent_visible = nullptr;
}
} break;
+
case NOTIFICATION_READY:
case NOTIFICATION_WM_SIZE_CHANGED: {
if (is_visible()) {
_update_child_rects();
}
} break;
+
case NOTIFICATION_WM_CLOSE_REQUEST: {
_cancel_pressed();
} break;
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index dad84461f4..e71ab64535 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -92,26 +92,30 @@ void FileDialog::_theme_changed() {
}
void FileDialog::_notification(int p_what) {
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (!is_visible()) {
- set_process_unhandled_input(false);
- }
- }
- if (p_what == NOTIFICATION_ENTER_TREE) {
- dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog")));
- if (vbox->is_layout_rtl()) {
- dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
- dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
- } else {
- dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
- dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
- }
- refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog")));
- show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog")));
- _theme_changed();
- }
- if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- update_filters();
+ switch (p_what) {
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ set_process_unhandled_input(false);
+ }
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog")));
+ if (vbox->is_layout_rtl()) {
+ dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
+ dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
+ } else {
+ dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog")));
+ dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog")));
+ }
+ refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog")));
+ show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog")));
+ _theme_changed();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ update_filters();
+ } break;
}
}
@@ -172,7 +176,7 @@ void FileDialog::update_dir() {
if (dir_access->get_current_dir().is_network_share_path()) {
_update_drives(false);
drives->add_item(RTR("Network"));
- drives->set_item_disabled(drives->get_item_count() - 1, true);
+ drives->set_item_disabled(-1, true);
drives->select(drives->get_item_count() - 1);
} else {
drives->select(dir_access->get_current_drive());
@@ -258,7 +262,8 @@ void FileDialog::_action_pressed() {
return;
}
- String f = dir_access->get_current_dir().plus_file(file->get_text());
+ String file_text = file->get_text();
+ String f = file_text.is_absolute_path() ? file_text : dir_access->get_current_dir().plus_file(file_text);
if ((mode == FILE_MODE_OPEN_ANY || mode == FILE_MODE_OPEN_FILE) && dir_access->file_exists(f)) {
emit_signal(SNAME("file_selected"), f);
@@ -914,9 +919,9 @@ void FileDialog::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "access", PROPERTY_HINT_ENUM, "Resources,User data,File system"), "set_access", "get_access");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "filters"), "set_filters", "get_filters");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_hidden_files"), "set_show_hidden_files", "is_showing_hidden_files");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir"), "set_current_dir", "get_current_dir");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file"), "set_current_file", "get_current_file");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path"), "set_current_path", "get_current_path");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_dir", PROPERTY_HINT_DIR, "", PROPERTY_USAGE_NONE), "set_current_dir", "get_current_dir");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_file", PROPERTY_HINT_FILE, "*", PROPERTY_USAGE_NONE), "set_current_file", "get_current_file");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_current_path", "get_current_path");
ADD_SIGNAL(MethodInfo("file_selected", PropertyInfo(Variant::STRING, "path")));
ADD_SIGNAL(MethodInfo("files_selected", PropertyInfo(Variant::PACKED_STRING_ARRAY, "paths")));
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index 36a6b262b0..7a50efe40f 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -70,7 +70,6 @@ private:
Button *makedir;
Access access = ACCESS_RESOURCES;
- //Button *action;
VBoxContainer *vbox;
FileMode mode;
LineEdit *dir;
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
index d1ac60b325..3bd21f96b2 100644
--- a/scene/gui/flow_container.cpp
+++ b/scene/gui/flow_container.cpp
@@ -28,8 +28,6 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "scene/gui/container.h"
-
#include "flow_container.h"
struct _LineData {
@@ -223,15 +221,41 @@ Size2 FlowContainer::get_minimum_size() const {
return minimum;
}
+Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> FlowContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void FlowContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
_resort();
update_minimum_size();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h
index e3ed423ae1..a2da43e071 100644
--- a/scene/gui/flow_container.h
+++ b/scene/gui/flow_container.h
@@ -31,7 +31,7 @@
#ifndef FLOW_CONTAINER_H
#define FLOW_CONTAINER_H
-class Container;
+#include "scene/gui/container.h"
class FlowContainer : public Container {
GDCLASS(FlowContainer, Container);
@@ -54,6 +54,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
FlowContainer(bool p_vertical = false);
};
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index bec3d58384..0690acbe16 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -287,84 +287,85 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) {
}
void GradientEdit::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) {
- picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE: {
+ if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) {
+ picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed));
+ }
+ [[fallthrough]];
}
- }
+ case NOTIFICATION_THEME_CHANGED: {
+ draw_spacing = BASE_SPACING * get_theme_default_base_scale();
+ draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
- draw_spacing = BASE_SPACING * get_theme_default_base_scale();
- draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale();
- }
+ case NOTIFICATION_DRAW: {
+ int w = get_size().x;
+ int h = get_size().y;
- if (p_what == NOTIFICATION_DRAW) {
- int w = get_size().x;
- int h = get_size().y;
+ if (w == 0 || h == 0) {
+ return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
+ }
- if (w == 0 || h == 0) {
- return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size.
- }
+ int total_w = get_size().width - get_size().height - draw_spacing;
- int total_w = get_size().width - get_size().height - draw_spacing;
+ // Draw checker pattern for ramp.
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
- // Draw checker pattern for ramp.
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true);
+ // Draw color ramp.
+ gradient_cache->set_points(points);
+ gradient_cache->set_interpolation_mode(interpolation_mode);
+ preview_texture->set_gradient(gradient_cache);
+ draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
- // Draw color ramp.
+ // Draw point markers.
+ for (int i = 0; i < points.size(); i++) {
+ Color col = points[i].color.inverted();
+ col.a = 0.9;
- gradient_cache->set_points(points);
- gradient_cache->set_interpolation_mode(interpolation_mode);
- preview_texture->set_gradient(gradient_cache);
- draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h));
+ draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
+ Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
+ draw_rect(rect, points[i].color, true);
+ draw_rect(rect, col, false);
+ if (grabbed == i) {
+ rect = rect.grow(-1);
+ if (has_focus()) {
+ draw_rect(rect, Color(1, 0, 0, 0.9), false);
+ } else {
+ draw_rect(rect, Color(0.6, 0, 0, 0.9), false);
+ }
- // Draw point markers.
- for (int i = 0; i < points.size(); i++) {
- Color col = points[i].color.inverted();
- col.a = 0.9;
-
- draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col);
- Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2);
- draw_rect(rect, points[i].color, true);
- draw_rect(rect, col, false);
- if (grabbed == i) {
- rect = rect.grow(-1);
- if (has_focus()) {
- draw_rect(rect, Color(1, 0, 0, 0.9), false);
- } else {
- draw_rect(rect, Color(0.6, 0, 0, 0.9), false);
+ rect = rect.grow(-1);
+ draw_rect(rect, col, false);
}
-
- rect = rect.grow(-1);
- draw_rect(rect, col, false);
}
- }
- //Draw "button" for color selector
- draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
- if (grabbed != -1) {
- //Draw with selection color
- draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
- } else {
- //if no color selected draw grey color with 'X' on top.
- draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
- draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
- }
+ // Draw "button" for color selector.
+ draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true);
+ if (grabbed != -1) {
+ // Draw with selection color.
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color);
+ } else {
+ // If no color selected draw grey color with 'X' on top.
+ draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1));
+ draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6));
+ }
- // Draw borders around color ramp if in focus.
- if (has_focus()) {
- draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
- }
- }
+ // Draw borders around color ramp if in focus.
+ if (has_focus()) {
+ draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+ draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6));
+ }
+ } break;
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- if (!is_visible()) {
- grabbing = false;
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ if (!is_visible()) {
+ grabbing = false;
+ }
+ } break;
}
}
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index 95575a8226..1394b4192f 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -423,82 +423,86 @@ void GraphEdit::remove_child_notify(Node *p_child) {
}
void GraphEdit::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
- port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal"));
- port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical"));
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED: {
+ port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal"));
+ port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical"));
+
+ zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
+ zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
+ zoom_plus->set_icon(get_theme_icon(SNAME("more")));
+ snap_button->set_icon(get_theme_icon(SNAME("snap")));
+ minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
+ layout_button->set_icon(get_theme_icon(SNAME("layout")));
+
+ zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
+ } break;
- zoom_minus->set_icon(get_theme_icon(SNAME("minus")));
- zoom_reset->set_icon(get_theme_icon(SNAME("reset")));
- zoom_plus->set_icon(get_theme_icon(SNAME("more")));
- snap_button->set_icon(get_theme_icon(SNAME("snap")));
- minimap_button->set_icon(get_theme_icon(SNAME("minimap")));
- layout_button->set_icon(get_theme_icon(SNAME("layout")));
+ case NOTIFICATION_READY: {
+ Size2 hmin = h_scroll->get_combined_minimum_size();
+ Size2 vmin = v_scroll->get_combined_minimum_size();
- zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale());
- }
- if (p_what == NOTIFICATION_READY) {
- Size2 hmin = h_scroll->get_combined_minimum_size();
- Size2 vmin = v_scroll->get_combined_minimum_size();
+ h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
+ h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
+ h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
- h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
- h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
+ v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
+ v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
+ } break;
- v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
- v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
- v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- }
- if (p_what == NOTIFICATION_DRAW) {
- draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size()));
+ case NOTIFICATION_DRAW: {
+ draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size()));
- if (is_using_snap()) {
- //draw grid
+ if (is_using_snap()) {
+ // Draw grid.
+ int snap = get_snap();
- int snap = get_snap();
+ Vector2 offset = get_scroll_ofs() / zoom;
+ Size2 size = get_size() / zoom;
- Vector2 offset = get_scroll_ofs() / zoom;
- Size2 size = get_size() / zoom;
+ Point2i from = (offset / float(snap)).floor();
+ Point2i len = (size / float(snap)).floor() + Vector2(1, 1);
- Point2i from = (offset / float(snap)).floor();
- Point2i len = (size / float(snap)).floor() + Vector2(1, 1);
+ Color grid_minor = get_theme_color(SNAME("grid_minor"));
+ Color grid_major = get_theme_color(SNAME("grid_major"));
- Color grid_minor = get_theme_color(SNAME("grid_minor"));
- Color grid_major = get_theme_color(SNAME("grid_major"));
+ for (int i = from.x; i < from.x + len.x; i++) {
+ Color color;
- for (int i = from.x; i < from.x + len.x; i++) {
- Color color;
+ if (ABS(i) % 10 == 0) {
+ color = grid_major;
+ } else {
+ color = grid_minor;
+ }
- if (ABS(i) % 10 == 0) {
- color = grid_major;
- } else {
- color = grid_minor;
+ float base_ofs = i * snap * zoom - offset.x * zoom;
+ draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color);
}
- float base_ofs = i * snap * zoom - offset.x * zoom;
- draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color);
- }
+ for (int i = from.y; i < from.y + len.y; i++) {
+ Color color;
- for (int i = from.y; i < from.y + len.y; i++) {
- Color color;
+ if (ABS(i) % 10 == 0) {
+ color = grid_major;
+ } else {
+ color = grid_minor;
+ }
- if (ABS(i) % 10 == 0) {
- color = grid_major;
- } else {
- color = grid_minor;
+ float base_ofs = i * snap * zoom - offset.y * zoom;
+ draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color);
}
-
- float base_ofs = i * snap * zoom - offset.y * zoom;
- draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color);
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_RESIZED) {
- _update_scroll();
- top_layer->update();
- minimap->update();
+ case NOTIFICATION_RESIZED: {
+ _update_scroll();
+ top_layer->update();
+ minimap->update();
+ } break;
}
}
@@ -1166,7 +1170,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
if (connecting) {
force_connection_drag_end();
} else {
- emit_signal(SNAME("popup_request"), get_screen_position() + b->get_position());
+ emit_signal(SNAME("popup_request"), b->get_position());
}
}
}
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index da973b46f0..b0d1944d6e 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -36,9 +36,7 @@
#include "scene/gui/graph_node.h"
#include "scene/gui/label.h"
#include "scene/gui/scroll_bar.h"
-#include "scene/gui/slider.h"
#include "scene/gui/spin_box.h"
-#include "scene/gui/texture_rect.h"
class GraphEdit;
class ViewPanner;
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 30f6cf4a14..ef0ac75cb4 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -31,6 +31,7 @@
#include "graph_node.h"
#include "core/string/translation.h"
+
#ifdef TOOLS_ENABLED
#include "graph_edit.h"
#endif
@@ -959,6 +960,25 @@ bool GraphNode::is_resizable() const {
return resizable;
}
+Vector<int> GraphNode::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> GraphNode::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_EXPAND);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphNode::set_title);
ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title);
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index b41fc7f5d4..7eb5f27cff 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -182,6 +182,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
bool is_resizing() const { return resizing; }
GraphNode();
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 465c9dac78..3c1f4bb93b 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -179,11 +179,12 @@ void GridContainer::_notification(int p_what) {
col_ofs += s.width + hsep;
}
}
-
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 0cb3249c1d..8c0f696a9f 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -83,6 +83,9 @@ int ItemList::add_icon_item(const Ref<Texture2D> &p_item, bool p_selectable) {
}
void ItemList::set_item_text(int p_idx, const String &p_text) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].text = p_text;
@@ -97,6 +100,9 @@ String ItemList::get_item_text(int p_idx) const {
}
void ItemList::set_item_text_direction(int p_idx, Control::TextDirection p_text_direction) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (items[p_idx].text_direction != p_text_direction) {
@@ -119,6 +125,9 @@ void ItemList::clear_item_opentype_features(int p_idx) {
}
void ItemList::set_item_opentype_feature(int p_idx, const String &p_name, int p_value) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
int32_t tag = TS->name_to_tag(p_name);
if (!items[p_idx].opentype_features.has(tag) || (int)items[p_idx].opentype_features[tag] != p_value) {
@@ -138,6 +147,9 @@ int ItemList::get_item_opentype_feature(int p_idx, const String &p_name) const {
}
void ItemList::set_item_language(int p_idx, const String &p_language) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].language != p_language) {
items.write[p_idx].language = p_language;
@@ -152,6 +164,9 @@ String ItemList::get_item_language(int p_idx) const {
}
void ItemList::set_item_tooltip_enabled(int p_idx, const bool p_enabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip_enabled = p_enabled;
}
@@ -162,6 +177,9 @@ bool ItemList::is_item_tooltip_enabled(int p_idx) const {
}
void ItemList::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip = p_tooltip;
@@ -175,6 +193,9 @@ String ItemList::get_item_tooltip(int p_idx) const {
}
void ItemList::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon = p_icon;
@@ -189,6 +210,9 @@ Ref<Texture2D> ItemList::get_item_icon(int p_idx) const {
}
void ItemList::set_item_icon_transposed(int p_idx, const bool p_transposed) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_transposed = p_transposed;
@@ -203,6 +227,9 @@ bool ItemList::is_item_icon_transposed(int p_idx) const {
}
void ItemList::set_item_icon_region(int p_idx, const Rect2 &p_region) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_region = p_region;
@@ -217,6 +244,9 @@ Rect2 ItemList::get_item_icon_region(int p_idx) const {
}
void ItemList::set_item_icon_modulate(int p_idx, const Color &p_modulate) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon_modulate = p_modulate;
@@ -230,6 +260,9 @@ Color ItemList::get_item_icon_modulate(int p_idx) const {
}
void ItemList::set_item_custom_bg_color(int p_idx, const Color &p_custom_bg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].custom_bg = p_custom_bg_color;
@@ -243,6 +276,9 @@ Color ItemList::get_item_custom_bg_color(int p_idx) const {
}
void ItemList::set_item_custom_fg_color(int p_idx, const Color &p_custom_fg_color) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].custom_fg = p_custom_fg_color;
@@ -256,6 +292,9 @@ Color ItemList::get_item_custom_fg_color(int p_idx) const {
}
void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tag_icon = p_tag_icon;
@@ -270,6 +309,9 @@ Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const {
}
void ItemList::set_item_selectable(int p_idx, bool p_selectable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].selectable = p_selectable;
@@ -281,6 +323,9 @@ bool ItemList::is_item_selectable(int p_idx) const {
}
void ItemList::set_item_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].disabled = p_disabled;
@@ -293,6 +338,9 @@ bool ItemList::is_item_disabled(int p_idx) const {
}
void ItemList::set_item_metadata(int p_idx, const Variant &p_metadata) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].metadata = p_metadata;
@@ -855,445 +903,451 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) {
}
void ItemList::_notification(int p_what) {
- if (p_what == NOTIFICATION_RESIZED) {
- shape_changed = true;
- update();
- }
+ switch (p_what) {
+ case NOTIFICATION_RESIZED: {
+ shape_changed = true;
+ update();
+ } break;
+
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED:
+ case NOTIFICATION_THEME_CHANGED: {
+ for (int i = 0; i < items.size(); i++) {
+ _shape(i);
+ }
+ shape_changed = true;
+ update();
+ } break;
- if ((p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED) || (p_what == NOTIFICATION_THEME_CHANGED)) {
- for (int i = 0; i < items.size(); i++) {
- _shape(i);
- }
- shape_changed = true;
- update();
- }
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
+ int mw = scroll_bar->get_minimum_size().x;
+ scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
+ scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
+ scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
- int mw = scroll_bar->get_minimum_size().x;
- scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw);
- scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
- scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP));
- scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM));
+ Size2 size = get_size();
- Size2 size = get_size();
+ int width = size.width - bg->get_minimum_size().width;
+ if (scroll_bar->is_visible()) {
+ width -= mw;
+ }
- int width = size.width - bg->get_minimum_size().width;
- if (scroll_bar->is_visible()) {
- width -= mw;
- }
+ draw_style_box(bg, Rect2(Point2(), size));
- draw_style_box(bg, Rect2(Point2(), size));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
+ int vseparation = get_theme_constant(SNAME("vseparation"));
+ int icon_margin = get_theme_constant(SNAME("icon_margin"));
+ int line_separation = get_theme_constant(SNAME("line_separation"));
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
- int hseparation = get_theme_constant(SNAME("hseparation"));
- int vseparation = get_theme_constant(SNAME("vseparation"));
- int icon_margin = get_theme_constant(SNAME("icon_margin"));
- int line_separation = get_theme_constant(SNAME("line_separation"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
+ Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
+ Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
+ bool rtl = is_layout_rtl();
- Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected"));
- Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused"));
- bool rtl = is_layout_rtl();
+ Color guide_color = get_theme_color(SNAME("guide_color"));
+ Color font_color = get_theme_color(SNAME("font_color"));
+ Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
- Color guide_color = get_theme_color(SNAME("guide_color"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_selected_color = get_theme_color(SNAME("font_selected_color"));
+ if (has_focus()) {
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
+ draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
+ }
- if (has_focus()) {
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true);
- draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size));
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false);
- }
+ if (shape_changed) {
+ float max_column_width = 0.0;
- if (shape_changed) {
- float max_column_width = 0.0;
+ //1- compute item minimum sizes
+ for (int i = 0; i < items.size(); i++) {
+ Size2 minsize;
+ if (items[i].icon.is_valid()) {
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ minsize = fixed_icon_size * icon_scale;
+ } else {
+ minsize = items[i].get_icon_size() * icon_scale;
+ }
- //1- compute item minimum sizes
- for (int i = 0; i < items.size(); i++) {
- Size2 minsize;
- if (items[i].icon.is_valid()) {
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- minsize = fixed_icon_size * icon_scale;
- } else {
- minsize = items[i].get_icon_size() * icon_scale;
+ if (!items[i].text.is_empty()) {
+ if (icon_mode == ICON_MODE_TOP) {
+ minsize.y += icon_margin;
+ } else {
+ minsize.x += icon_margin;
+ }
+ }
}
if (!items[i].text.is_empty()) {
- if (icon_mode == ICON_MODE_TOP) {
- minsize.y += icon_margin;
- } else {
- minsize.x += icon_margin;
+ int max_width = -1;
+ if (fixed_column_width) {
+ max_width = fixed_column_width;
+ } else if (same_column_width) {
+ max_width = items[i].rect_cache.size.x;
}
- }
- }
+ items.write[i].text_buf->set_width(max_width);
+ Size2 s = items[i].text_buf->get_size();
- if (!items[i].text.is_empty()) {
- int max_width = -1;
- if (fixed_column_width) {
- max_width = fixed_column_width;
- } else if (same_column_width) {
- max_width = items[i].rect_cache.size.x;
- }
- items.write[i].text_buf->set_width(max_width);
- Size2 s = items[i].text_buf->get_size();
+ if (icon_mode == ICON_MODE_TOP) {
+ minsize.x = MAX(minsize.x, s.width);
+ if (max_text_lines > 0) {
+ minsize.y += s.height + line_separation * max_text_lines;
+ } else {
+ minsize.y += s.height;
+ }
- if (icon_mode == ICON_MODE_TOP) {
- minsize.x = MAX(minsize.x, s.width);
- if (max_text_lines > 0) {
- minsize.y += s.height + line_separation * max_text_lines;
} else {
- minsize.y += s.height;
+ minsize.y = MAX(minsize.y, s.height);
+ minsize.x += s.width;
}
+ }
- } else {
- minsize.y = MAX(minsize.y, s.height);
- minsize.x += s.width;
+ if (fixed_column_width > 0) {
+ minsize.x = fixed_column_width;
}
+ max_column_width = MAX(max_column_width, minsize.x);
+
+ // elements need to adapt to the selected size
+ minsize.y += vseparation;
+ minsize.x += hseparation;
+ items.write[i].rect_cache.size = minsize;
+ items.write[i].min_rect_cache.size = minsize;
}
- if (fixed_column_width > 0) {
- minsize.x = fixed_column_width;
+ int fit_size = size.x - bg->get_minimum_size().width - mw;
+
+ //2-attempt best fit
+ current_columns = 0x7FFFFFFF;
+ if (max_columns > 0) {
+ current_columns = max_columns;
}
- max_column_width = MAX(max_column_width, minsize.x);
- // elements need to adapt to the selected size
- minsize.y += vseparation;
- minsize.x += hseparation;
- items.write[i].rect_cache.size = minsize;
- items.write[i].min_rect_cache.size = minsize;
- }
+ while (true) {
+ //repeat until all fits
+ bool all_fit = true;
+ Vector2 ofs;
+ int col = 0;
+ int max_h = 0;
+ separators.clear();
+ for (int i = 0; i < items.size(); i++) {
+ if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
+ //went past
+ current_columns = MAX(col, 1);
+ all_fit = false;
+ break;
+ }
- int fit_size = size.x - bg->get_minimum_size().width - mw;
+ if (same_column_width) {
+ items.write[i].rect_cache.size.x = max_column_width;
+ }
+ items.write[i].rect_cache.position = ofs;
+ max_h = MAX(max_h, items[i].rect_cache.size.y);
+ ofs.x += items[i].rect_cache.size.x + hseparation;
+ col++;
+ if (col == current_columns) {
+ if (i < items.size() - 1) {
+ separators.push_back(ofs.y + max_h + vseparation / 2);
+ }
- //2-attempt best fit
- current_columns = 0x7FFFFFFF;
- if (max_columns > 0) {
- current_columns = max_columns;
- }
+ for (int j = i; j >= 0 && col > 0; j--, col--) {
+ items.write[j].rect_cache.size.y = max_h;
+ }
- while (true) {
- //repeat until all fits
- bool all_fit = true;
- Vector2 ofs;
- int col = 0;
- int max_h = 0;
- separators.clear();
- for (int i = 0; i < items.size(); i++) {
- if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) {
- //went past
- current_columns = MAX(col, 1);
- all_fit = false;
- break;
+ ofs.x = 0;
+ ofs.y += max_h + vseparation;
+ col = 0;
+ max_h = 0;
+ }
}
- if (same_column_width) {
- items.write[i].rect_cache.size.x = max_column_width;
+ for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
+ items.write[j].rect_cache.size.y = max_h;
}
- items.write[i].rect_cache.position = ofs;
- max_h = MAX(max_h, items[i].rect_cache.size.y);
- ofs.x += items[i].rect_cache.size.x + hseparation;
- col++;
- if (col == current_columns) {
- if (i < items.size() - 1) {
- separators.push_back(ofs.y + max_h + vseparation / 2);
- }
- for (int j = i; j >= 0 && col > 0; j--, col--) {
- items.write[j].rect_cache.size.y = max_h;
+ if (all_fit) {
+ float page = MAX(0, size.height - bg->get_minimum_size().height);
+ float max = MAX(page, ofs.y + max_h);
+ if (auto_height) {
+ auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
}
+ scroll_bar->set_max(max);
+ scroll_bar->set_page(page);
+ if (max <= page) {
+ scroll_bar->set_value(0);
+ scroll_bar->hide();
+ } else {
+ scroll_bar->show();
- ofs.x = 0;
- ofs.y += max_h + vseparation;
- col = 0;
- max_h = 0;
+ if (do_autoscroll_to_bottom) {
+ scroll_bar->set_value(max);
+ }
+ }
+ break;
}
}
- for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) {
- items.write[j].rect_cache.size.y = max_h;
- }
+ update_minimum_size();
+ shape_changed = false;
+ }
- if (all_fit) {
- float page = MAX(0, size.height - bg->get_minimum_size().height);
- float max = MAX(page, ofs.y + max_h);
- if (auto_height) {
- auto_height_value = ofs.y + max_h + bg->get_minimum_size().height;
- }
- scroll_bar->set_max(max);
- scroll_bar->set_page(page);
- if (max <= page) {
- scroll_bar->set_value(0);
- scroll_bar->hide();
- } else {
- scroll_bar->show();
+ //ensure_selected_visible needs to be checked before we draw the list.
+ if (ensure_selected_visible && current >= 0 && current < items.size()) {
+ Rect2 r = items[current].rect_cache;
+ int from = scroll_bar->get_value();
+ int to = from + scroll_bar->get_page();
- if (do_autoscroll_to_bottom) {
- scroll_bar->set_value(max);
- }
- }
- break;
+ if (r.position.y < from) {
+ scroll_bar->set_value(r.position.y);
+ } else if (r.position.y + r.size.y > to) {
+ scroll_bar->set_value(r.position.y + r.size.y - (to - from));
}
}
- update_minimum_size();
- shape_changed = false;
- }
+ ensure_selected_visible = false;
- //ensure_selected_visible needs to be checked before we draw the list.
- if (ensure_selected_visible && current >= 0 && current < items.size()) {
- Rect2 r = items[current].rect_cache;
- int from = scroll_bar->get_value();
- int to = from + scroll_bar->get_page();
-
- if (r.position.y < from) {
- scroll_bar->set_value(r.position.y);
- } else if (r.position.y + r.size.y > to) {
- scroll_bar->set_value(r.position.y + r.size.y - (to - from));
- }
- }
+ Vector2 base_ofs = bg->get_offset();
+ base_ofs.y -= int(scroll_bar->get_value());
- ensure_selected_visible = false;
+ const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
- Vector2 base_ofs = bg->get_offset();
- base_ofs.y -= int(scroll_bar->get_value());
-
- const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there
-
- int first_item_visible;
- {
- // do a binary search to find the first item whose rect reaches below clip.position.y
- int lo = 0;
- int hi = items.size();
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- const Rect2 &rcache = items[mid].rect_cache;
- if (rcache.position.y + rcache.size.y < clip.position.y) {
- lo = mid + 1;
- } else {
- hi = mid;
+ int first_item_visible;
+ {
+ // do a binary search to find the first item whose rect reaches below clip.position.y
+ int lo = 0;
+ int hi = items.size();
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ const Rect2 &rcache = items[mid].rect_cache;
+ if (rcache.position.y + rcache.size.y < clip.position.y) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
}
+ // we might have ended up with column 2, or 3, ..., so let's find the first column
+ while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) {
+ lo -= 1;
+ }
+ first_item_visible = lo;
}
- // we might have ended up with column 2, or 3, ..., so let's find the first column
- while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) {
- lo -= 1;
- }
- first_item_visible = lo;
- }
- for (int i = first_item_visible; i < items.size(); i++) {
- Rect2 rcache = items[i].rect_cache;
+ for (int i = first_item_visible; i < items.size(); i++) {
+ Rect2 rcache = items[i].rect_cache;
- if (rcache.position.y > clip.position.y + clip.size.y) {
- break; // done
- }
-
- if (!clip.intersects(rcache)) {
- continue;
- }
-
- if (current_columns == 1) {
- rcache.size.width = width - rcache.position.x;
- }
+ if (rcache.position.y > clip.position.y + clip.size.y) {
+ break; // done
+ }
- if (items[i].selected) {
- Rect2 r = rcache;
- r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ if (!clip.intersects(rcache)) {
+ continue;
}
- draw_style_box(sbsel, r);
- }
- if (items[i].custom_bg.a > 0.001) {
- Rect2 r = rcache;
- r.position += base_ofs;
-
- // Size rect to make the align the temperature colors
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ if (current_columns == 1) {
+ rcache.size.width = width - rcache.position.x;
}
- draw_rect(r, items[i].custom_bg);
- }
+ if (items[i].selected) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
- Vector2 text_ofs;
- if (items[i].icon.is_valid()) {
- Size2 icon_size;
- //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale;
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
+ }
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- icon_size = fixed_icon_size * icon_scale;
- } else {
- icon_size = items[i].get_icon_size() * icon_scale;
+ draw_style_box(sbsel, r);
}
+ if (items[i].custom_bg.a > 0.001) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
- Vector2 icon_ofs;
+ // Size rect to make the align the temperature colors
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
- Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
+ }
- if (icon_mode == ICON_MODE_TOP) {
- pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
- pos.y += icon_margin;
- text_ofs.y = icon_size.height + icon_margin * 2;
- } else {
- pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
- text_ofs.x = icon_size.width + icon_margin;
+ draw_rect(r, items[i].custom_bg);
}
- Rect2 draw_rect = Rect2(pos, icon_size);
+ Vector2 text_ofs;
+ if (items[i].icon.is_valid()) {
+ Size2 icon_size;
+ //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale;
- if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
- Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size);
- draw_rect.position += adj.position;
- draw_rect.size = adj.size;
- }
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ icon_size = fixed_icon_size * icon_scale;
+ } else {
+ icon_size = items[i].get_icon_size() * icon_scale;
+ }
- Color modulate = items[i].icon_modulate;
- if (items[i].disabled) {
- modulate.a *= 0.5;
- }
+ Vector2 icon_ofs;
- // If the icon is transposed, we have to switch the size so that it is drawn correctly
- if (items[i].icon_transposed) {
- Size2 size_tmp = draw_rect.size;
- draw_rect.size.x = size_tmp.y;
- draw_rect.size.y = size_tmp.x;
- }
+ Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs;
- Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region);
+ if (icon_mode == ICON_MODE_TOP) {
+ pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2);
+ pos.y += icon_margin;
+ text_ofs.y = icon_size.height + icon_margin * 2;
+ } else {
+ pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2);
+ text_ofs.x = icon_size.width + icon_margin;
+ }
- if (rtl) {
- draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x;
- }
- draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed);
- }
+ Rect2 draw_rect = Rect2(pos, icon_size);
- if (items[i].tag_icon.is_valid()) {
- Point2 draw_pos = items[i].rect_cache.position;
- if (rtl) {
- draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width();
- }
- draw_texture(items[i].tag_icon, draw_pos + base_ofs);
- }
+ if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) {
+ Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size);
+ draw_rect.position += adj.position;
+ draw_rect.size = adj.size;
+ }
- if (!items[i].text.is_empty()) {
- int max_len = -1;
+ Color modulate = items[i].icon_modulate;
+ if (items[i].disabled) {
+ modulate.a *= 0.5;
+ }
- Vector2 size2 = items[i].text_buf->get_size();
- if (fixed_column_width) {
- max_len = fixed_column_width;
- } else if (same_column_width) {
- max_len = items[i].rect_cache.size.x;
- } else {
- max_len = size2.x;
- }
+ // If the icon is transposed, we have to switch the size so that it is drawn correctly
+ if (items[i].icon_transposed) {
+ Size2 size_tmp = draw_rect.size;
+ draw_rect.size.x = size_tmp.y;
+ draw_rect.size.y = size_tmp.x;
+ }
- Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
- if (items[i].disabled) {
- modulate.a *= 0.5;
- }
+ Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region);
- if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
- text_ofs += base_ofs;
- text_ofs += items[i].rect_cache.position;
+ if (rtl) {
+ draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x;
+ }
+ draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed);
+ }
+ if (items[i].tag_icon.is_valid()) {
+ Point2 draw_pos = items[i].rect_cache.position;
if (rtl) {
- text_ofs.x = size.width - text_ofs.x - max_len;
+ draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width();
}
+ draw_texture(items[i].tag_icon, draw_pos + base_ofs);
+ }
- items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ if (!items[i].text.is_empty()) {
+ int max_len = -1;
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ Vector2 size2 = items[i].text_buf->get_size();
+ if (fixed_column_width) {
+ max_len = fixed_column_width;
+ } else if (same_column_width) {
+ max_len = items[i].rect_cache.size.x;
+ } else {
+ max_len = size2.x;
}
- items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
- } else {
- if (fixed_column_width > 0) {
- size2.x = MIN(size2.x, fixed_column_width);
+ Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color);
+ if (items[i].disabled) {
+ modulate.a *= 0.5;
}
- if (icon_mode == ICON_MODE_TOP) {
- text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
- } else {
- text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
- }
+ if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
+ text_ofs += base_ofs;
+ text_ofs += items[i].rect_cache.position;
- text_ofs += base_ofs;
- text_ofs += items[i].rect_cache.position;
+ if (rtl) {
+ text_ofs.x = size.width - text_ofs.x - max_len;
+ }
- if (rtl) {
- text_ofs.x = size.width - text_ofs.x - max_len;
- }
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- items.write[i].text_buf->set_width(max_len);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
- if (rtl) {
- items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
} else {
- items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ if (fixed_column_width > 0) {
+ size2.x = MIN(size2.x, fixed_column_width);
+ }
+
+ if (icon_mode == ICON_MODE_TOP) {
+ text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2;
+ } else {
+ text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2;
+ }
+
+ text_ofs += base_ofs;
+ text_ofs += items[i].rect_cache.position;
+
+ if (rtl) {
+ text_ofs.x = size.width - text_ofs.x - max_len;
+ }
+
+ items.write[i].text_buf->set_width(width - text_ofs.x);
+
+ if (rtl) {
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ } else {
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ }
+
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ }
+
+ if (width - text_ofs.x > 0) {
+ items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ }
}
+ }
- if (outline_size > 0 && font_outline_color.a > 0) {
- items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color);
+ if (select_mode == SELECT_MULTI && i == current) {
+ Rect2 r = rcache;
+ r.position += base_ofs;
+ r.position.y -= vseparation / 2;
+ r.size.y += vseparation;
+ r.position.x -= hseparation / 2;
+ r.size.x += hseparation;
+
+ if (rtl) {
+ r.position.x = size.width - r.position.x - r.size.x;
}
- items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate);
+ draw_style_box(cursor, r);
}
}
- if (select_mode == SELECT_MULTI && i == current) {
- Rect2 r = rcache;
- r.position += base_ofs;
- r.position.y -= vseparation / 2;
- r.size.y += vseparation;
- r.position.x -= hseparation / 2;
- r.size.x += hseparation;
-
- if (rtl) {
- r.position.x = size.width - r.position.x - r.size.x;
+ int first_visible_separator = 0;
+ {
+ // do a binary search to find the first separator that is below clip_position.y
+ int lo = 0;
+ int hi = separators.size();
+ while (lo < hi) {
+ const int mid = (lo + hi) / 2;
+ if (separators[mid] < clip.position.y) {
+ lo = mid + 1;
+ } else {
+ hi = mid;
+ }
}
-
- draw_style_box(cursor, r);
+ first_visible_separator = lo;
}
- }
- int first_visible_separator = 0;
- {
- // do a binary search to find the first separator that is below clip_position.y
- int lo = 0;
- int hi = separators.size();
- while (lo < hi) {
- const int mid = (lo + hi) / 2;
- if (separators[mid] < clip.position.y) {
- lo = mid + 1;
- } else {
- hi = mid;
+ for (int i = first_visible_separator; i < separators.size(); i++) {
+ if (separators[i] > clip.position.y + clip.size.y) {
+ break; // done
}
- }
- first_visible_separator = lo;
- }
- for (int i = first_visible_separator; i < separators.size(); i++) {
- if (separators[i] > clip.position.y + clip.size.y) {
- break; // done
+ const int y = base_ofs.y + separators[i];
+ draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
}
-
- const int y = base_ofs.y + separators[i];
- draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color);
- }
+ } break;
}
}
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index 77e910870f..96735678c1 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -99,7 +99,7 @@ private:
SelectMode select_mode = SELECT_SINGLE;
IconMode icon_mode = ICON_MODE_LEFT;
VScrollBar *scroll_bar;
- TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_NO_TRIMMING;
+ TextParagraph::OverrunBehavior text_overrun_behavior = TextParagraph::OVERRUN_TRIM_ELLIPSIS;
uint64_t search_time_msec = 0;
String search_string;
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index fab420d593..419901d5ea 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -82,9 +82,11 @@ void Label::_shape() {
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"), SNAME("Label"));
int width = (get_size().width - style->get_minimum_size().width);
- if (dirty) {
+ if (dirty || font_dirty) {
String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
- TS->shaped_text_clear(text_rid);
+ if (dirty) {
+ TS->shaped_text_clear(text_rid);
+ }
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
TS->shaped_text_set_direction(text_rid, is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
} else {
@@ -97,9 +99,17 @@ void Label::_shape() {
if (visible_chars >= 0 && visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
text = text.substr(0, visible_chars);
}
- TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
+ if (dirty) {
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
+ } else {
+ int spans = TS->shaped_get_span_count(text_rid);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(text_rid, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
+ font_dirty = false;
lines_dirty = true;
}
@@ -253,168 +263,227 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col
}
void Label::_notification(int p_what) {
- if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- String new_text = atr(text);
- if (new_text == xl_text) {
- return; // Nothing new.
- }
- xl_text = new_text;
- if (percent_visible < 1) {
- visible_chars = get_total_character_count() * percent_visible;
- }
- dirty = true;
+ switch (p_what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ String new_text = atr(text);
+ if (new_text == xl_text) {
+ return; // Nothing new.
+ }
+ xl_text = new_text;
+ if (percent_visible < 1) {
+ visible_chars = get_total_character_count() * percent_visible;
+ }
+ dirty = true;
- update();
- }
+ update();
+ } break;
- if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) {
- update();
- }
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ update();
+ } break;
- if (p_what == NOTIFICATION_DRAW) {
- if (clip) {
- RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
- }
+ case NOTIFICATION_DRAW: {
+ if (clip) {
+ RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
+ }
- if (dirty || lines_dirty) {
- _shape();
- }
+ if (dirty || font_dirty || lines_dirty) {
+ _shape();
+ }
- RID ci = get_canvas_item();
-
- Size2 string_size;
- Size2 size = get_size();
- Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
- Ref<Font> font = get_theme_font(SNAME("font"));
- Color font_color = get_theme_color(SNAME("font_color"));
- Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
- Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
- int line_spacing = get_theme_constant(SNAME("line_spacing"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
- bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
- bool rtl_layout = is_layout_rtl();
-
- style->draw(ci, Rect2(Point2(0, 0), get_size()));
-
- float total_h = 0.0;
- int lines_visible = 0;
-
- // Get number of lines to fit to the height.
- for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
- if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
- break;
+ RID ci = get_canvas_item();
+
+ Size2 string_size;
+ Size2 size = get_size();
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
+ Ref<Font> font = get_theme_font(SNAME("font"));
+ Color font_color = get_theme_color(SNAME("font_color"));
+ Color font_shadow_color = get_theme_color(SNAME("font_shadow_color"));
+ Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y")));
+ int line_spacing = get_theme_constant(SNAME("line_spacing"));
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size"));
+ bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL);
+ bool rtl_layout = is_layout_rtl();
+
+ style->draw(ci, Rect2(Point2(0, 0), get_size()));
+
+ float total_h = 0.0;
+ int lines_visible = 0;
+
+ // Get number of lines to fit to the height.
+ for (int64_t i = lines_skipped; i < lines_rid.size(); i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) {
+ break;
+ }
+ lines_visible++;
}
- lines_visible++;
- }
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
- lines_visible = max_lines_visible;
- }
+ if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
+ lines_visible = max_lines_visible;
+ }
- int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
- bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING);
- bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !rtl_layout));
- bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && rtl_layout));
-
- // Get real total height.
- int total_glyphs = 0;
- total_h = 0;
- for (int64_t i = lines_skipped; i < last_line; i++) {
- total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
- total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
- }
- int visible_glyphs = total_glyphs * percent_visible;
- int processed_glyphs = 0;
- total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
-
- int vbegin = 0, vsep = 0;
- if (lines_visible > 0) {
- switch (vertical_alignment) {
- case VERTICAL_ALIGNMENT_TOP: {
- // Nothing.
- } break;
- case VERTICAL_ALIGNMENT_CENTER: {
- vbegin = (size.y - (total_h - line_spacing)) / 2;
- vsep = 0;
-
- } break;
- case VERTICAL_ALIGNMENT_BOTTOM: {
- vbegin = size.y - (total_h - line_spacing);
- vsep = 0;
-
- } break;
- case VERTICAL_ALIGNMENT_FILL: {
- vbegin = 0;
- if (lines_visible > 1) {
- vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
- } else {
+ int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
+ bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING);
+ bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !rtl_layout));
+ bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && rtl_layout));
+
+ // Get real total height.
+ int total_glyphs = 0;
+ total_h = 0;
+ for (int64_t i = lines_skipped; i < last_line; i++) {
+ total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing;
+ total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+ }
+ int visible_glyphs = total_glyphs * percent_visible;
+ int processed_glyphs = 0;
+ total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM);
+
+ int vbegin = 0, vsep = 0;
+ if (lines_visible > 0) {
+ switch (vertical_alignment) {
+ case VERTICAL_ALIGNMENT_TOP: {
+ // Nothing.
+ } break;
+ case VERTICAL_ALIGNMENT_CENTER: {
+ vbegin = (size.y - (total_h - line_spacing)) / 2;
vsep = 0;
- }
- } break;
+ } break;
+ case VERTICAL_ALIGNMENT_BOTTOM: {
+ vbegin = size.y - (total_h - line_spacing);
+ vsep = 0;
+
+ } break;
+ case VERTICAL_ALIGNMENT_FILL: {
+ vbegin = 0;
+ if (lines_visible > 1) {
+ vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
+ } else {
+ vsep = 0;
+ }
+
+ } break;
+ }
}
- }
- Vector2 ofs;
- ofs.y = style->get_offset().y + vbegin;
- for (int i = lines_skipped; i < last_line; i++) {
- Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
- ofs.x = 0;
- ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
- switch (horizontal_alignment) {
- case HORIZONTAL_ALIGNMENT_FILL:
- if (rtl && autowrap_mode != AUTOWRAP_OFF) {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
- } else {
- ofs.x = style->get_offset().x;
- }
- break;
- case HORIZONTAL_ALIGNMENT_LEFT: {
- if (rtl_layout) {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
- } else {
- ofs.x = style->get_offset().x;
- }
- } break;
- case HORIZONTAL_ALIGNMENT_CENTER: {
- ofs.x = int(size.width - line_size.width) / 2;
- } break;
- case HORIZONTAL_ALIGNMENT_RIGHT: {
- if (rtl_layout) {
- ofs.x = style->get_offset().x;
- } else {
- ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ Vector2 ofs;
+ ofs.y = style->get_offset().y + vbegin;
+ for (int i = lines_skipped; i < last_line; i++) {
+ Size2 line_size = TS->shaped_text_get_size(lines_rid[i]);
+ ofs.x = 0;
+ ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP);
+ switch (horizontal_alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ if (rtl && autowrap_mode != AUTOWRAP_OFF) {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ } else {
+ ofs.x = style->get_offset().x;
+ }
+ break;
+ case HORIZONTAL_ALIGNMENT_LEFT: {
+ if (rtl_layout) {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ } else {
+ ofs.x = style->get_offset().x;
+ }
+ } break;
+ case HORIZONTAL_ALIGNMENT_CENTER: {
+ ofs.x = int(size.width - line_size.width) / 2;
+ } break;
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
+ if (rtl_layout) {
+ ofs.x = style->get_offset().x;
+ } else {
+ ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width);
+ }
+ } break;
+ }
+
+ const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
+ int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
+
+ int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]);
+ int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]);
+
+ const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]);
+ int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+
+ // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
+ int processed_glyphs_ol = processed_glyphs;
+ if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
+ Vector2 offset = ofs;
+ // Draw RTL ellipsis string when necessary.
+ if (rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += ellipsis_glyphs[gl_idx].advance;
+ }
+ }
}
- } break;
- }
- const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]);
- int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]);
+ // Draw main text.
+ for (int j = 0; j < gl_size; j++) {
+ // Trim when necessary.
+ if (trim_pos >= 0) {
+ if (rtl) {
+ if (j < trim_pos) {
+ continue;
+ }
+ } else {
+ if (j >= trim_pos) {
+ break;
+ }
+ }
+ }
+ for (int k = 0; k < glyphs[j].repeat; k++) {
+ bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
- int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]);
- int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]);
+ // Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += glyphs[j].advance;
+ }
+ }
+ // Draw LTR ellipsis string when necessary.
+ if (!rtl && ellipsis_pos >= 0) {
+ for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
+ for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ //Draw glyph outlines and shadow.
+ if (!skip) {
+ draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ }
+ processed_glyphs_ol++;
+ offset.x += ellipsis_glyphs[gl_idx].advance;
+ }
+ }
+ }
+ }
- const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]);
- int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]);
+ // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
- // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps.
- int processed_glyphs_ol = processed_glyphs;
- if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) {
- Vector2 offset = ofs;
// Draw RTL ellipsis string when necessary.
if (rtl && ellipsis_pos >= 0) {
for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
//Draw glyph outlines and shadow.
if (!skip) {
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
}
- processed_glyphs_ol++;
- offset.x += ellipsis_glyphs[gl_idx].advance;
+ processed_glyphs++;
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
@@ -434,104 +503,48 @@ void Label::_notification(int p_what) {
}
}
for (int k = 0; k < glyphs[j].repeat; k++) {
- bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
// Draw glyph outlines and shadow.
if (!skip) {
- draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ draw_glyph(glyphs[j], ci, font_color, ofs);
}
- processed_glyphs_ol++;
- offset.x += glyphs[j].advance;
+ processed_glyphs++;
+ ofs.x += glyphs[j].advance;
}
}
// Draw LTR ellipsis string when necessary.
if (!rtl && ellipsis_pos >= 0) {
for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
+ bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
//Draw glyph outlines and shadow.
if (!skip) {
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
}
- processed_glyphs_ol++;
- offset.x += ellipsis_glyphs[gl_idx].advance;
+ processed_glyphs++;
+ ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
}
+ ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
}
+ } break;
- // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps.
-
- // Draw RTL ellipsis string when necessary.
- if (rtl && ellipsis_pos >= 0) {
- for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
- //Draw glyph outlines and shadow.
- if (!skip) {
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
- }
- processed_glyphs++;
- ofs.x += ellipsis_glyphs[gl_idx].advance;
- }
- }
- }
-
- // Draw main text.
- for (int j = 0; j < gl_size; j++) {
- // Trim when necessary.
- if (trim_pos >= 0) {
- if (rtl) {
- if (j < trim_pos) {
- continue;
- }
- } else {
- if (j >= trim_pos) {
- break;
- }
- }
- }
- for (int k = 0; k < glyphs[j].repeat; k++) {
- bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
-
- // Draw glyph outlines and shadow.
- if (!skip) {
- draw_glyph(glyphs[j], ci, font_color, ofs);
- }
- processed_glyphs++;
- ofs.x += glyphs[j].advance;
- }
- }
- // Draw LTR ellipsis string when necessary.
- if (!rtl && ellipsis_pos >= 0) {
- for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) {
- for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) {
- bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs));
- //Draw glyph outlines and shadow.
- if (!skip) {
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
- }
- processed_glyphs++;
- ofs.x += ellipsis_glyphs[gl_idx].advance;
- }
- }
- }
- ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM);
- }
- }
+ case NOTIFICATION_THEME_CHANGED: {
+ font_dirty = true;
+ update();
+ } break;
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- dirty = true;
- update();
- }
- if (p_what == NOTIFICATION_RESIZED) {
- lines_dirty = true;
+ case NOTIFICATION_RESIZED: {
+ lines_dirty = true;
+ } break;
}
}
Size2 Label::get_minimum_size() const {
// don't want to mutable everything
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -555,7 +568,7 @@ int Label::get_line_count() const {
if (!is_inside_tree()) {
return 1;
}
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -630,7 +643,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (text_direction != p_text_direction) {
text_direction = p_text_direction;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -638,7 +651,7 @@ void Label::set_text_direction(Control::TextDirection p_text_direction) {
void Label::set_structured_text_bidi_override(Control::StructuredTextParser p_parser) {
if (st_parser != p_parser) {
st_parser = p_parser;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -649,7 +662,7 @@ Control::StructuredTextParser Label::get_structured_text_bidi_override() const {
void Label::set_structured_text_bidi_override_options(Array p_args) {
st_args = p_args;
- dirty = true;
+ font_dirty = true;
update();
}
@@ -663,7 +676,7 @@ Control::TextDirection Label::get_text_direction() const {
void Label::clear_opentype_features() {
opentype_features.clear();
- dirty = true;
+ font_dirty = true;
update();
}
@@ -671,7 +684,7 @@ void Label::set_opentype_feature(const String &p_name, int p_value) {
int32_t tag = TS->name_to_tag(p_name);
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -798,7 +811,7 @@ int Label::get_max_lines_visible() const {
}
int Label::get_total_character_count() const {
- if (dirty || lines_dirty) {
+ if (dirty || font_dirty || lines_dirty) {
const_cast<Label *>(this)->_shape();
}
@@ -814,13 +827,13 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
- dirty = true;
+ font_dirty = true;
update();
}
} else {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
- dirty = true;
+ font_dirty = true;
update();
}
}
@@ -914,20 +927,24 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_GROUP("Locale", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
- ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible");
+
+ // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied.
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible");
+
+ ADD_GROUP("Locale", "");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
+
ADD_GROUP("Structured Text", "structured_text_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 354e9c664d..0b931b3084 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -73,6 +73,7 @@ private:
bool lines_dirty = true;
bool dirty = true;
+ bool font_dirty = true;
RID text_rid;
Vector<RID> lines_rid;
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 3aae3377bc..da39a3d387 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -647,8 +647,8 @@ void LineEdit::_notification(int p_what) {
#ifdef TOOLS_ENABLED
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint() && !get_tree()->is_node_being_edited(this)) {
- set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
+ set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed"));
if (!EditorSettings::get_singleton()->is_connected("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed))) {
EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &LineEdit::_editor_settings_changed));
@@ -656,31 +656,37 @@ void LineEdit::_notification(int p_what) {
}
} break;
#endif
+
case NOTIFICATION_RESIZED: {
_fit_to_width();
scroll_offset = 0;
set_caret_column(get_caret_column());
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_shape();
update();
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED: {
placeholder_translated = atr(placeholder);
_shape();
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
} break;
+
case NOTIFICATION_DRAW: {
if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) {
draw_caret = false;
@@ -841,7 +847,8 @@ void LineEdit::_notification(int p_what) {
// Draw carets.
ofs.x = x_ofs + scroll_offset;
if (draw_caret || drag_caret_force_displayed) {
- const int caret_width = get_theme_constant(SNAME("caret_width")) * get_theme_default_base_scale();
+ // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
+ const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale());
if (ime_text.length() == 0) {
// Normal caret.
@@ -923,6 +930,7 @@ void LineEdit::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_FOCUS_ENTER: {
if (!caret_force_displayed) {
if (caret_blink_enabled) {
@@ -942,6 +950,7 @@ void LineEdit::_notification(int p_what) {
show_virtual_keyboard();
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled && !caret_force_displayed) {
caret_blink_timer->stop();
@@ -964,6 +973,7 @@ void LineEdit::_notification(int p_what) {
deselect();
}
} break;
+
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
@@ -974,10 +984,12 @@ void LineEdit::_notification(int p_what) {
update();
}
} break;
- case Control::NOTIFICATION_DRAG_BEGIN: {
+
+ case NOTIFICATION_DRAG_BEGIN: {
drag_action = true;
} break;
- case Control::NOTIFICATION_DRAG_END: {
+
+ case NOTIFICATION_DRAG_END: {
if (is_drag_successful()) {
if (selection.drag_attempt) {
selection.drag_attempt = false;
@@ -1606,7 +1618,7 @@ Size2 LineEdit::get_minimum_size() const {
Size2 min_size;
// Minimum size of text.
- int em_space_size = font->get_char_size('M', 0, font_size).x;
+ float em_space_size = font->get_char_size('M', 0, font_size).x;
min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size;
if (expand_to_text_length) {
@@ -1932,8 +1944,8 @@ PopupMenu *LineEdit::get_menu() const {
void LineEdit::_editor_settings_changed() {
#ifdef TOOLS_ENABLED
- set_caret_blink_enabled(EDITOR_DEF("text_editor/appearance/caret/caret_blink", false));
- set_caret_blink_speed(EDITOR_DEF("text_editor/appearance/caret/caret_blink_speed", 0.65));
+ set_caret_blink_enabled(EDITOR_GET("text_editor/appearance/caret/caret_blink"));
+ set_caret_blink_speed(EDITOR_GET("text_editor/appearance/caret/caret_blink_speed"));
#endif
}
@@ -2425,7 +2437,7 @@ void LineEdit::_ensure_menu() {
}
}
-LineEdit::LineEdit() {
+LineEdit::LineEdit(const String &p_placeholder) {
text_rid = TS->create_shaped_text();
_create_undo_state();
@@ -2440,6 +2452,8 @@ LineEdit::LineEdit() {
caret_blink_timer->connect("timeout", callable_mp(this, &LineEdit::_toggle_draw_caret));
set_caret_blink_enabled(false);
+ set_placeholder(p_placeholder);
+
set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable.
}
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 1519c09d73..444c9a1c50 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -332,7 +332,7 @@ public:
void show_virtual_keyboard();
- LineEdit();
+ LineEdit(const String &p_placeholder = String());
~LineEdit();
};
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 0ff05faf85..dc4f09d22d 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "link_button.h"
+
#include "core/string/translation.h"
void LinkButton::_shape() {
@@ -148,18 +149,20 @@ void LinkButton::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
xl_text = atr(text);
_shape();
-
update_minimum_size();
update();
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
update();
} break;
+
case NOTIFICATION_THEME_CHANGED: {
_shape();
update_minimum_size();
update();
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2 size = get_size();
@@ -230,7 +233,6 @@ void LinkButton::_notification(int p_what) {
draw_line(Vector2(0, y), Vector2(width, y), color, text_buf->get_line_underline_thickness());
}
}
-
} break;
}
}
@@ -315,8 +317,10 @@ void LinkButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options");
}
-LinkButton::LinkButton() {
+LinkButton::LinkButton(const String &p_text) {
text_buf.instantiate();
set_focus_mode(FOCUS_NONE);
set_default_cursor_shape(CURSOR_POINTING_HAND);
+
+ set_text(p_text);
}
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 7d302e967d..f996558f32 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -32,7 +32,6 @@
#define LINKBUTTON_H
#include "scene/gui/base_button.h"
-#include "scene/resources/bit_map.h"
#include "scene/resources/text_line.h"
class LinkButton : public BaseButton {
@@ -91,7 +90,7 @@ public:
void set_underline_mode(UnderlineMode p_underline_mode);
UnderlineMode get_underline_mode() const;
- LinkButton();
+ LinkButton(const String &p_text = String());
};
VARIANT_ENUM_CAST(LinkButton::UnderlineMode);
diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp
index 7b696ddb84..fac37a8634 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -65,6 +65,24 @@ Size2 MarginContainer::get_minimum_size() const {
return max;
}
+Vector<int> MarginContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> MarginContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void MarginContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_SORT_CHILDREN: {
@@ -89,6 +107,7 @@ void MarginContainer::_notification(int p_what) {
fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h));
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h
index 3a2f0fa8b3..f8a3c5bb11 100644
--- a/scene/gui/margin_container.h
+++ b/scene/gui/margin_container.h
@@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
MarginContainer();
};
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index f7805136f9..7e724e4d71 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -36,7 +36,7 @@
void MenuButton::unhandled_key_input(const Ref<InputEvent> &p_event) {
ERR_FAIL_COND(p_event.is_null());
- if (!_is_focus_owner_in_shorcut_context()) {
+ if (!_is_focus_owner_in_shortcut_context()) {
return;
}
@@ -98,7 +98,13 @@ void MenuButton::pressed() {
popup->set_position(gp);
popup->set_parent_rect(Rect2(Point2(gp - popup->get_position()), size));
- popup->take_mouse_focus();
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (popup->get_item_count() > 0 &&
+ ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) {
+ popup->set_current_index(0);
+ }
+
popup->popup();
}
@@ -130,11 +136,16 @@ int MenuButton::get_item_count() const {
void MenuButton::_notification(int p_what) {
switch (p_what) {
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ } break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
popup->hide();
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
Vector2i mouse_pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted;
MenuButton *menu_btn_other = Object::cast_to<MenuButton>(get_viewport()->gui_find_control(mouse_pos));
@@ -216,7 +227,8 @@ void MenuButton::set_disable_shortcuts(bool p_disabled) {
disable_shortcuts = p_disabled;
}
-MenuButton::MenuButton() {
+MenuButton::MenuButton(const String &p_text) :
+ Button(p_text) {
set_flat(true);
set_toggle_mode(true);
set_disable_shortcuts(false);
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 3647a69d33..9cfb780255 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -67,7 +67,7 @@ public:
void set_item_count(int p_count);
int get_item_count() const;
- MenuButton();
+ MenuButton(const String &p_text = String());
~MenuButton();
};
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index 779d1307f5..4f34ece86f 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -34,18 +34,20 @@
#include "servers/rendering_server.h"
void NinePatchRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (texture.is_null()) {
- return;
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (texture.is_null()) {
+ return;
+ }
- Rect2 rect = Rect2(Point2(), get_size());
- Rect2 src_rect = region_rect;
+ Rect2 rect = Rect2(Point2(), get_size());
+ Rect2 src_rect = region_rect;
- texture->get_rect_region(rect, src_rect, rect, src_rect);
+ texture->get_rect_region(rect, src_rect, rect, src_rect);
- RID ci = get_canvas_item();
- RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center);
+ RID ci = get_canvas_item();
+ RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center);
+ } break;
}
}
@@ -93,10 +95,6 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) {
}
texture = p_tex;
update();
- /*
- if (texture.is_valid())
- texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites
- */
update_minimum_size();
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index c80de04c01..b3804e73d9 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -91,8 +91,12 @@ void OptionButton::_notification(int p_what) {
}
arrow->draw(ci, ofs, clr);
} break;
+
case NOTIFICATION_TRANSLATION_CHANGED:
- case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ popup->set_layout_direction((Window::LayoutDirection)get_layout_direction());
+ [[fallthrough]];
+ }
case NOTIFICATION_THEME_CHANGED: {
if (has_theme_icon(SNAME("arrow"))) {
if (is_layout_rtl()) {
@@ -104,6 +108,7 @@ void OptionButton::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible_in_tree()) {
popup->hide();
@@ -115,6 +120,11 @@ void OptionButton::_notification(int p_what) {
bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
bool valid;
popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
@@ -133,6 +143,11 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) {
bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0] == "popup") {
+ String property = components[2];
+ if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") {
+ return false;
+ }
+
bool valid;
r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
return valid;
@@ -148,14 +163,6 @@ void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button");
- pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0);
- p_list->push_back(pi);
-
- pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i));
- pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
- p_list->push_back(pi);
-
pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
@@ -181,7 +188,17 @@ void OptionButton::pressed() {
Size2 size = get_size() * get_viewport()->get_canvas_transform().get_scale();
popup->set_position(get_screen_position() + Size2(0, size.height * get_global_transform().get_scale().y));
popup->set_size(Size2(size.width, 0));
- popup->set_current_index(current);
+
+ // If not triggered by the mouse, start the popup with the checked item selected.
+ if (popup->get_item_count() > 0) {
+ if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) ||
+ (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) {
+ popup->set_current_index(current > -1 ? current : 0);
+ } else {
+ popup->scroll_to_item(current > -1 ? current : 0);
+ }
+ }
+
popup->popup();
}
@@ -257,7 +274,20 @@ bool OptionButton::is_item_disabled(int p_idx) const {
void OptionButton::set_item_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
+
+ int count_old = get_item_count();
+ if (p_count == count_old) {
+ return;
+ }
+
popup->set_item_count(p_count);
+
+ if (p_count > count_old) {
+ for (int i = count_old; i < p_count; i++) {
+ popup->set_item_as_radio_checkable(i, true);
+ }
+ }
+
notify_property_list_changed();
}
@@ -287,7 +317,7 @@ void OptionButton::_select(int p_which, bool p_emit) {
current = NONE_SELECTED;
set_text("");
- set_icon(NULL);
+ set_icon(nullptr);
} else {
ERR_FAIL_INDEX(p_which, popup->get_item_count());
@@ -382,7 +412,8 @@ void OptionButton::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index")));
}
-OptionButton::OptionButton() {
+OptionButton::OptionButton(const String &p_text) :
+ Button(p_text) {
set_toggle_mode(true);
set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (is_layout_rtl()) {
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index adf2bb90ef..5352fe18a6 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -94,7 +94,7 @@ public:
virtual void get_translatable_strings(List<String> *p_strings) const override;
- OptionButton();
+ OptionButton(const String &p_text = String());
~OptionButton();
};
diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp
index 86858fdc78..1ac6cf57ab 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -30,35 +30,16 @@
#include "panel.h"
-#include "core/string/print_string.h"
-
void Panel::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- Ref<StyleBox> style = mode == MODE_BACKGROUND ? get_theme_stylebox(SNAME("panel")) : get_theme_stylebox(SNAME("panel_fg"));
- style->draw(ci, Rect2(Point2(), get_size()));
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
+ style->draw(ci, Rect2(Point2(), get_size()));
+ } break;
}
}
-void Panel::set_mode(Mode p_mode) {
- mode = p_mode;
- update();
-}
-
-Panel::Mode Panel::get_mode() const {
- return mode;
-}
-
-void Panel::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Panel::set_mode);
- ClassDB::bind_method(D_METHOD("get_mode"), &Panel::get_mode);
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Background,Foreground"), "set_mode", "get_mode");
-
- BIND_ENUM_CONSTANT(MODE_BACKGROUND);
- BIND_ENUM_CONSTANT(MODE_FOREGROUND);
-}
-
Panel::Panel() {
// Has visible stylebox, so stop by default.
set_mouse_filter(MOUSE_FILTER_STOP);
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index 37f14c250c..5d2e912680 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -36,26 +36,11 @@
class Panel : public Control {
GDCLASS(Panel, Control);
-public:
- enum Mode {
- MODE_BACKGROUND,
- MODE_FOREGROUND,
- };
-
-private:
- Mode mode = MODE_BACKGROUND;
-
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
- void set_mode(Mode p_mode);
- Mode get_mode() const;
-
Panel();
};
-VARIANT_ENUM_CAST(Panel::Mode)
-
#endif // PANEL_H
diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp
index 463ad3c513..fe01712a89 100644
--- a/scene/gui/panel_container.cpp
+++ b/scene/gui/panel_container.cpp
@@ -60,47 +60,67 @@ Size2 PanelContainer::get_minimum_size() const {
return ms;
}
-void PanelContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
- Ref<StyleBox> style;
-
- if (has_theme_stylebox(SNAME("panel"))) {
- style = get_theme_stylebox(SNAME("panel"));
- } else {
- style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
- }
+Vector<int> PanelContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
- style->draw(ci, Rect2(Point2(), get_size()));
- }
+Vector<int> PanelContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- Ref<StyleBox> style;
+void PanelContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
+ Ref<StyleBox> style;
- if (has_theme_stylebox(SNAME("panel"))) {
- style = get_theme_stylebox(SNAME("panel"));
- } else {
- style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
- }
+ if (has_theme_stylebox(SNAME("panel"))) {
+ style = get_theme_stylebox(SNAME("panel"));
+ } else {
+ style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
+ }
- Size2 size = get_size();
- Point2 ofs;
- if (style.is_valid()) {
- size -= style->get_minimum_size();
- ofs += style->get_offset();
- }
+ style->draw(ci, Rect2(Point2(), get_size()));
+ } break;
- for (int i = 0; i < get_child_count(); i++) {
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c || !c->is_visible_in_tree()) {
- continue;
+ case NOTIFICATION_SORT_CHILDREN: {
+ Ref<StyleBox> style;
+
+ if (has_theme_stylebox(SNAME("panel"))) {
+ style = get_theme_stylebox(SNAME("panel"));
+ } else {
+ style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer"));
}
- if (c->is_set_as_top_level()) {
- continue;
+
+ Size2 size = get_size();
+ Point2 ofs;
+ if (style.is_valid()) {
+ size -= style->get_minimum_size();
+ ofs += style->get_offset();
}
- fit_child_in_rect(c, Rect2(ofs, size));
- }
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c || !c->is_visible_in_tree()) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+
+ fit_child_in_rect(c, Rect2(ofs, size));
+ }
+ } break;
}
}
diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h
index a5ff74cebb..8f07ce38eb 100644
--- a/scene/gui/panel_container.h
+++ b/scene/gui/panel_container.h
@@ -42,6 +42,9 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
PanelContainer();
};
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 7c03fcbb37..b9e3e7814e 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -42,26 +42,30 @@ void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
}
void Popup::_initialize_visible_parents() {
- visible_parents.clear();
-
- Window *parent_window = this;
- while (parent_window) {
- parent_window = parent_window->get_parent_visible_window();
- if (parent_window) {
- visible_parents.push_back(parent_window);
- parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ if (is_embedded()) {
+ visible_parents.clear();
+
+ Window *parent_window = this;
+ while (parent_window) {
+ parent_window = parent_window->get_parent_visible_window();
+ if (parent_window) {
+ visible_parents.push_back(parent_window);
+ parent_window->connect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ parent_window->connect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
}
}
}
void Popup::_deinitialize_visible_parents() {
- for (uint32_t i = 0; i < visible_parents.size(); ++i) {
- visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
- visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
- }
+ if (is_embedded()) {
+ for (uint32_t i = 0; i < visible_parents.size(); ++i) {
+ visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused));
+ visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents));
+ }
- visible_parents.clear();
+ visible_parents.clear();
+ }
}
void Popup::_notification(int p_what) {
@@ -74,19 +78,19 @@ void Popup::_notification(int p_what) {
emit_signal(SNAME("popup_hide"));
popped_up = false;
}
-
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
if (has_focus()) {
popped_up = true;
}
} break;
+
case NOTIFICATION_EXIT_TREE: {
_deinitialize_visible_parents();
} break;
- case NOTIFICATION_WM_CLOSE_REQUEST: {
- _close_pressed();
- } break;
+
+ case NOTIFICATION_WM_CLOSE_REQUEST:
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
_close_pressed();
} break;
@@ -94,7 +98,7 @@ void Popup::_notification(int p_what) {
}
void Popup::_parent_focused() {
- if (popped_up && close_on_parent_focus) {
+ if (popped_up && get_flag(FLAG_POPUP)) {
_close_pressed();
}
}
@@ -107,23 +111,7 @@ void Popup::_close_pressed() {
call_deferred(SNAME("hide"));
}
-void Popup::set_as_minsize() {
- set_size(get_contents_minimum_size());
-}
-
-void Popup::set_close_on_parent_focus(bool p_close) {
- close_on_parent_focus = p_close;
-}
-
-bool Popup::get_close_on_parent_focus() {
- return close_on_parent_focus;
-}
-
void Popup::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_close_on_parent_focus", "close"), &Popup::set_close_on_parent_focus);
- ClassDB::bind_method(D_METHOD("get_close_on_parent_focus"), &Popup::get_close_on_parent_focus);
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "close_on_parent_focus"), "set_close_on_parent_focus", "get_close_on_parent_focus");
-
ADD_SIGNAL(MethodInfo("popup_hide"));
}
@@ -184,6 +172,7 @@ Popup::Popup() {
set_transient(true);
set_flag(FLAG_BORDERLESS, true);
set_flag(FLAG_RESIZE_DISABLED, true);
+ set_flag(FLAG_POPUP, true);
connect("window_input", callable_mp(this, &Popup::_input_from_window));
}
@@ -241,13 +230,20 @@ void PopupPanel::_update_child_rects() {
}
void PopupPanel::_notification(int p_what) {
- if (p_what == NOTIFICATION_THEME_CHANGED) {
- panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
- } else if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_ENTER_TREE) {
- panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
- _update_child_rects();
- } else if (p_what == NOTIFICATION_WM_SIZE_CHANGED) {
- _update_child_rects();
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
+ } break;
+
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_READY: {
+ panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name()));
+ _update_child_rects();
+ } break;
+
+ case NOTIFICATION_WM_SIZE_CHANGED: {
+ _update_child_rects();
+ } break;
}
}
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 5678043b23..c45f4ddc24 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -42,7 +42,6 @@ class Popup : public Window {
LocalVector<Window *> visible_parents;
bool popped_up = false;
- bool close_on_parent_focus = true;
void _input_from_window(const Ref<InputEvent> &p_event);
@@ -59,11 +58,6 @@ protected:
static void _bind_methods();
public:
- void set_as_minsize();
-
- void set_close_on_parent_focus(bool p_close);
- bool get_close_on_parent_focus();
-
Popup();
~Popup();
};
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index fc853a3df4..4220066b20 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -30,6 +30,7 @@
#include "popup_menu.h"
+#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
@@ -67,7 +68,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
size.width += items[i].h_ofs;
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
has_check = true;
}
@@ -108,10 +109,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int PopupMenu::_get_item_height(int p_item) const {
ERR_FAIL_INDEX_V(p_item, items.size(), 0);
- ERR_FAIL_COND_V(p_item < 0, 0);
int icon_height = items[p_item].get_icon_size().height;
- if (items[p_item].checkable_type) {
+ if (items[p_item].checkable_type && !items[p_item].separator) {
icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height()));
}
@@ -141,23 +141,6 @@ int PopupMenu::_get_items_total_height() const {
return items_total_height - vsep;
}
-void PopupMenu::_scroll_to_item(int p_item) {
- ERR_FAIL_INDEX(p_item, items.size());
- ERR_FAIL_COND(p_item < 0);
-
- // Scroll item into view (upwards)
- if (items[p_item]._ofs_cache < -control->get_position().y) {
- int amnt_over = items[p_item]._ofs_cache + control->get_position().y;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-
- // Scroll item into view (downwards)
- if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) {
- int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height;
- scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over);
- }
-}
-
int PopupMenu::_get_mouse_over(const Point2 &p_over) const {
if (p_over.x < 0 || p_over.x >= get_size().width) {
return -1;
@@ -192,7 +175,7 @@ void PopupMenu::_activate_submenu(int p_over) {
Popup *submenu_popup = Object::cast_to<Popup>(n);
ERR_FAIL_COND_MSG(!submenu_popup, "Item subnode is not a Popup: " + items[p_over].submenu + ".");
if (submenu_popup->is_visible()) {
- return; //already visible!
+ return; // Already visible.
}
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
@@ -203,15 +186,17 @@ void PopupMenu::_activate_submenu(int p_over) {
float scroll_offset = control->get_position().y;
- Point2 submenu_pos;
+ submenu_popup->reset_size(); // Shrink the popup size to its contents.
Size2 submenu_size = submenu_popup->get_size();
+
+ Point2 submenu_pos;
if (control->is_layout_rtl()) {
submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset);
} else {
submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset);
}
- // Fix pos if going outside parent rect
+ // Fix pos if going outside parent rect.
if (submenu_pos.x < get_parent_rect().position.x) {
submenu_pos.x = this_pos.x + submenu_size.width;
}
@@ -220,27 +205,39 @@ void PopupMenu::_activate_submenu(int p_over) {
submenu_pos.x = this_pos.x - submenu_size.width;
}
- submenu_popup->set_close_on_parent_focus(false);
submenu_popup->set_position(submenu_pos);
- submenu_popup->set_as_minsize(); // Shrink the popup size to its contents.
- submenu_popup->popup();
- // Set autohide areas
PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup);
- if (submenu_pum) {
- submenu_pum->take_mouse_focus();
- // Make the position of the parent popup relative to submenu popup
- this_rect.position = this_rect.position - submenu_pum->get_position();
-
- // Autohide area above the submenu item
- submenu_pum->clear_autohide_areas();
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
-
- // If there is an area below the submenu item, add an autohide area there.
- if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
- int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
- submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
- }
+ if (!submenu_pum) {
+ submenu_popup->popup();
+ return;
+ }
+
+ // If not triggered by the mouse, start the popup with its first item selected.
+ if (submenu_pum->get_item_count() > 0 && Input::get_singleton()->is_action_just_pressed("ui_accept")) {
+ submenu_pum->set_current_index(0);
+ }
+
+ submenu_pum->popup();
+
+ // Set autohide areas.
+
+ Rect2 safe_area = this_rect;
+ safe_area.position.y += items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2;
+ safe_area.size.y = items[p_over]._height_cache;
+ DisplayServer::get_singleton()->window_set_popup_safe_rect(submenu_popup->get_window_id(), safe_area);
+
+ // Make the position of the parent popup relative to submenu popup.
+ this_rect.position = this_rect.position - submenu_pum->get_position();
+
+ // Autohide area above the submenu item.
+ submenu_pum->clear_autohide_areas();
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y, this_rect.size.x, items[p_over]._ofs_cache + scroll_offset + style->get_offset().height - vsep / 2));
+
+ // If there is an area below the submenu item, add an autohide area there.
+ if (items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset <= control->get_size().height) {
+ int from = items[p_over]._ofs_cache + items[p_over]._height_cache + scroll_offset + vsep / 2 + style->get_offset().height;
+ submenu_pum->add_autohide_area(Rect2(this_rect.position.x, this_rect.position.y + from, this_rect.size.x, this_rect.size.y - from));
}
}
@@ -266,7 +263,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -280,7 +277,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -298,7 +295,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
match_found = true;
@@ -312,7 +309,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (!items[i].separator && !items[i].disabled) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -462,7 +459,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
if (items[i].text.findn(search_string) == 0) {
mouse_over = i;
emit_signal(SNAME("id_focused"), i);
- _scroll_to_item(i);
+ scroll_to_item(i);
control->update();
set_input_as_handled();
break;
@@ -486,7 +483,7 @@ void PopupMenu::_draw_items() {
bool rtl = control->is_layout_rtl();
Ref<StyleBox> style = get_theme_stylebox(SNAME("panel"));
Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover"));
- // In Item::checkable_type enum order (less the non-checkable member)
+ // In Item::checkable_type enum order (less the non-checkable member).
Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) };
Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) };
Ref<Texture2D> submenu;
@@ -515,6 +512,10 @@ void PopupMenu::_draw_items() {
float icon_ofs = 0.0;
bool has_check = false;
for (int i = 0; i < items.size(); i++) {
+ if (items[i].separator) {
+ continue;
+ }
+
icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs);
if (items[i].checkable_type) {
@@ -558,29 +559,33 @@ void PopupMenu::_draw_items() {
if (items[i].separator) {
int sep_h = separator->get_center_size().height + separator->get_minimum_size().height;
int sep_ofs = Math::floor((h - sep_h) / 2.0);
- if (!text.is_empty()) {
- int text_size = items[i].text_buf->get_size().width;
- int text_center = display_width / 2;
- int text_left = text_center - text_size / 2;
- int text_right = text_center + text_size / 2;
- if (text_left > item_ofs.x) {
- labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h)));
+ if (!text.is_empty() || !items[i].icon.is_null()) {
+ int content_size = items[i].text_buf->get_size().width;
+ if (!items[i].icon.is_null()) {
+ content_size += icon_size.width + hseparation;
}
- if (text_right < display_width) {
- labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h)));
+
+ int content_center = display_width / 2;
+ int content_left = content_center - content_size / 2;
+ int content_right = content_center + content_size / 2;
+ if (content_left > item_ofs.x) {
+ labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h)));
+ }
+ if (content_right < display_width) {
+ labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h)));
}
} else {
separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h)));
}
}
- Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1);
+ Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1);
// For non-separator items, add some padding for the content.
item_ofs.x += item_start_padding;
// Checkboxes
- if (items[i].checkable_type) {
+ if (items[i].checkable_type && !items[i].separator) {
Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr();
if (rtl) {
icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color);
@@ -589,16 +594,28 @@ void PopupMenu::_draw_items() {
}
}
+ int separator_ofs = (display_width - items[i].text_buf->get_size().width) / 2;
+
// Icon
if (!items[i].icon.is_null()) {
- if (rtl) {
- items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (items[i].separator) {
+ separator_ofs -= (icon_size.width + hseparation) / 2;
+
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
} else {
- items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ if (rtl) {
+ items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ } else {
+ items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color);
+ }
}
}
- // Submenu arrow on right hand side
+ // Submenu arrow on right hand side.
if (!items[i].submenu.is_empty()) {
if (rtl) {
submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color);
@@ -612,8 +629,11 @@ void PopupMenu::_draw_items() {
int outline_size = get_theme_constant(SNAME("outline_size"));
if (items[i].separator) {
if (!text.is_empty()) {
- int center = (display_width - items[i].text_buf->get_size().width) / 2;
- Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0));
+ if (!rtl && !items[i].icon.is_null()) {
+ text_pos.x += icon_size.width + hseparation;
+ }
+
if (outline_size > 0 && font_outline_color.a > 0) {
items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
}
@@ -650,7 +670,7 @@ void PopupMenu::_draw_items() {
items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color);
}
- // Cache the item vertical offset from the first item and the height
+ // Cache the item vertical offset from the first item and the height.
items.write[i]._ofs_cache = ofs.y;
items.write[i]._height_cache = h;
@@ -715,11 +735,12 @@ void PopupMenu::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent());
if (pm) {
- // Inherit submenu's popup delay time from parent menu
+ // Inherit submenu's popup delay time from parent menu.
float pm_delay = pm->get_submenu_popup_delay();
set_submenu_popup_delay(pm_delay);
}
} break;
+
case NOTIFICATION_THEME_CHANGED:
case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
@@ -732,23 +753,25 @@ void PopupMenu::_notification(int p_what) {
child_controls_changed();
control->update();
} break;
+
case NOTIFICATION_WM_MOUSE_ENTER: {
grab_focus();
} break;
+
case NOTIFICATION_WM_MOUSE_EXIT: {
if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) {
mouse_over = -1;
control->update();
}
} break;
+
case NOTIFICATION_POST_POPUP: {
initial_button_mask = Input::get_singleton()->get_mouse_button_mask();
during_grabbed_click = (bool)initial_button_mask;
} break;
- case NOTIFICATION_WM_SIZE_CHANGED: {
- } break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
- //only used when using operating system windows
+ // Only used when using operating system windows.
if (!is_embedded() && autohide_areas.size()) {
Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
mouse_pos -= get_position();
@@ -761,6 +784,7 @@ void PopupMenu::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (!is_visible()) {
if (mouse_over >= 0) {
@@ -810,7 +834,7 @@ void PopupMenu::_notification(int p_what) {
#define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \
item.text = p_label; \
item.xl_text = atr(p_label); \
- item.id = p_id == -1 ? items.size() - 1 : p_id; \
+ item.id = p_id == -1 ? items.size() : p_id; \
item.accel = p_accel;
void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) {
@@ -892,7 +916,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int
_ref_shortcut(p_shortcut); \
item.text = p_shortcut->get_name(); \
item.xl_text = atr(item.text); \
- item.id = p_id == -1 ? items.size() - 1 : p_id; \
+ item.id = p_id == -1 ? items.size() : p_id; \
item.shortcut = p_shortcut; \
item.shortcut_is_global = p_global;
@@ -961,7 +985,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
Item item;
item.text = p_label;
item.xl_text = atr(p_label);
- item.id = p_id == -1 ? items.size() - 1 : p_id;
+ item.id = p_id == -1 ? items.size() : p_id;
item.submenu = p_submenu;
items.push_back(item);
_shape_item(items.size() - 1);
@@ -975,6 +999,9 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu,
/* Methods to modify existing items. */
void PopupMenu::set_item_text(int p_idx, const String &p_text) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].text = p_text;
items.write[p_idx].xl_text = atr(p_text);
@@ -985,6 +1012,9 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) {
}
void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_text_direction) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
if (items[p_item].text_direction != p_text_direction) {
@@ -995,6 +1025,9 @@ void PopupMenu::set_item_text_direction(int p_item, Control::TextDirection p_tex
}
void PopupMenu::clear_item_opentype_features(int p_item) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
items.write[p_item].opentype_features.clear();
items.write[p_item].dirty = true;
@@ -1002,6 +1035,9 @@ void PopupMenu::clear_item_opentype_features(int p_item) {
}
void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int p_value) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
int32_t tag = TS->name_to_tag(p_name);
if (!items[p_item].opentype_features.has(tag) || (int)items[p_item].opentype_features[tag] != p_value) {
@@ -1012,6 +1048,9 @@ void PopupMenu::set_item_opentype_feature(int p_item, const String &p_name, int
}
void PopupMenu::set_item_language(int p_item, const String &p_language) {
+ if (p_item < 0) {
+ p_item += get_item_count();
+ }
ERR_FAIL_INDEX(p_item, items.size());
if (items[p_item].language != p_language) {
items.write[p_item].language = p_language;
@@ -1021,6 +1060,9 @@ void PopupMenu::set_item_language(int p_item, const String &p_language) {
}
void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].icon = p_icon;
@@ -1029,6 +1071,9 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) {
}
void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checked = p_checked;
@@ -1038,6 +1083,9 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) {
}
void PopupMenu::set_item_id(int p_idx, int p_id) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].id = p_id;
@@ -1046,6 +1094,9 @@ void PopupMenu::set_item_id(int p_idx, int p_id) {
}
void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].accel = p_accel;
items.write[p_idx].dirty = true;
@@ -1055,6 +1106,9 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) {
}
void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].metadata = p_meta;
control->update();
@@ -1062,6 +1116,9 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) {
}
void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].disabled = p_disabled;
control->update();
@@ -1069,6 +1126,9 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) {
}
void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].submenu = p_submenu;
control->update();
@@ -1177,6 +1237,9 @@ int PopupMenu::get_item_state(int p_idx) const {
}
void PopupMenu::set_item_as_separator(int p_idx, bool p_separator) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].separator = p_separator;
control->update();
@@ -1188,24 +1251,36 @@ bool PopupMenu::is_item_separator(int p_idx) const {
}
void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE;
control->update();
}
void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE;
control->update();
}
void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].tooltip = p_tooltip;
control->update();
}
void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bool p_global) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].shortcut.is_valid()) {
_unref_shortcut(items[p_idx].shortcut);
@@ -1222,6 +1297,9 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo
}
void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].h_ofs = p_offset;
control->update();
@@ -1229,12 +1307,18 @@ void PopupMenu::set_item_h_offset(int p_idx, int p_offset) {
}
void PopupMenu::set_item_multistate(int p_idx, int p_state) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].state = p_state;
control->update();
}
void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) {
+ if (p_idx < 0) {
+ p_idx += get_item_count();
+ }
ERR_FAIL_INDEX(p_idx, items.size());
items.write[p_idx].shortcut_is_disabled = p_disabled;
control->update();
@@ -1272,7 +1356,7 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
void PopupMenu::set_current_index(int p_idx) {
ERR_FAIL_INDEX(p_idx, items.size());
mouse_over = p_idx;
- _scroll_to_item(mouse_over);
+ scroll_to_item(mouse_over);
control->update();
}
@@ -1300,6 +1384,20 @@ int PopupMenu::get_item_count() const {
return items.size();
}
+void PopupMenu::scroll_to_item(int p_item) {
+ ERR_FAIL_INDEX(p_item, items.size());
+
+ // Scroll item into view (upwards).
+ if (items[p_item]._ofs_cache - scroll_container->get_v_scroll() < -control->get_position().y) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + control->get_position().y);
+ }
+
+ // Scroll item into view (downwards).
+ if (items[p_item]._ofs_cache + items[p_item]._height_cache - scroll_container->get_v_scroll() > -control->get_position().y + scroll_container->get_size().height) {
+ scroll_container->set_v_scroll(items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y);
+ }
+}
+
bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) {
Key code = Key::NONE;
Ref<InputEventKey> k = p_event;
@@ -1573,7 +1671,7 @@ bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) {
} else if (property == "id") {
set_item_id(item_index, p_value);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
set_item_disabled(item_index, p_value);
return true;
} else if (property == "separator") {
@@ -1646,7 +1744,7 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const {
} else if (property == "id") {
r_ret = get_item_id(item_index);
return true;
- } else if (components[1] == "disabled") {
+ } else if (property == "disabled") {
r_ret = is_item_disabled(item_index);
return true;
} else if (property == "separator") {
@@ -1747,10 +1845,13 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_tooltip", "index"), &PopupMenu::get_item_tooltip);
ClassDB::bind_method(D_METHOD("get_item_shortcut", "index"), &PopupMenu::get_item_shortcut);
+ ClassDB::bind_method(D_METHOD("set_current_index", "index"), &PopupMenu::set_current_index);
ClassDB::bind_method(D_METHOD("get_current_index"), &PopupMenu::get_current_index);
ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count);
ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "index"), &PopupMenu::scroll_to_item);
+
ClassDB::bind_method(D_METHOD("remove_item", "index"), &PopupMenu::remove_item);
ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 7c2212d82d..5ce55209d4 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -103,7 +103,6 @@ class PopupMenu : public Popup {
int _get_item_height(int p_item) const;
int _get_items_total_height() const;
- void _scroll_to_item(int p_item);
void _shape_item(int p_item);
@@ -218,6 +217,8 @@ public:
void set_item_count(int p_count);
int get_item_count() const;
+ void scroll_to_item(int p_item);
+
bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false);
void activate_item(int p_item);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index c20fb0d7a8..20b3513375 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "progress_bar.h"
+
#include "scene/resources/text_line.h"
Size2 ProgressBar::get_minimum_size() const {
@@ -52,36 +53,38 @@ Size2 ProgressBar::get_minimum_size() const {
}
void ProgressBar::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
- Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
- Ref<Font> font = get_theme_font(SNAME("font"));
- int font_size = get_theme_font_size(SNAME("font_size"));
- Color font_color = get_theme_color(SNAME("font_color"));
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg"));
+ Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg"));
+ Ref<Font> font = get_theme_font(SNAME("font"));
+ int font_size = get_theme_font_size(SNAME("font_size"));
+ Color font_color = get_theme_color(SNAME("font_color"));
- draw_style_box(bg, Rect2(Point2(), get_size()));
- float r = get_as_ratio();
- int mp = fg->get_minimum_size().width;
- int p = r * (get_size().width - mp);
- if (p > 0) {
- if (is_layout_rtl()) {
- draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height)));
- } else {
- draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ draw_style_box(bg, Rect2(Point2(), get_size()));
+ float r = get_as_ratio();
+ int mp = fg->get_minimum_size().width;
+ int p = r * (get_size().width - mp);
+ if (p > 0) {
+ if (is_layout_rtl()) {
+ draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height)));
+ } else {
+ draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height)));
+ }
}
- }
- if (percent_visible) {
- String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
- TextLine tl = TextLine(txt, font, font_size);
- Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
- if (outline_size > 0 && font_outline_color.a > 0) {
- tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+ if (percent_visible) {
+ String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign();
+ TextLine tl = TextLine(txt, font, font_size);
+ Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round();
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color);
+ }
+ tl.draw(get_canvas_item(), text_pos, font_color);
}
- tl.draw(get_canvas_item(), text_pos, font_color);
- }
+ } break;
}
}
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index 879f25c8d8..2fb6452a97 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -40,6 +40,9 @@ TypedArray<String> Range::get_configuration_warnings() const {
return warnings;
}
+void Range::_value_changed(double p_value) {
+ GDVIRTUAL_CALL(_value_changed, p_value);
+}
void Range::_value_changed_notify() {
_value_changed(shared->val);
emit_signal(SNAME("value_changed"), shared->val);
@@ -279,6 +282,8 @@ void Range::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_greater"), "set_allow_greater", "is_greater_allowed");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_lesser"), "set_allow_lesser", "is_lesser_allowed");
+ GDVIRTUAL_BIND(_value_changed);
+
ADD_LINKED_PROPERTY("min_value", "value");
ADD_LINKED_PROPERTY("min_value", "max_value");
ADD_LINKED_PROPERTY("min_value", "page");
diff --git a/scene/gui/range.h b/scene/gui/range.h
index c27eeee13c..597c50ca26 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -62,12 +62,14 @@ class Range : public Control {
void _validate_values();
protected:
- virtual void _value_changed(double) {}
+ virtual void _value_changed(double p_value);
static void _bind_methods();
bool _rounded_values = false;
+ GDVIRTUAL1(_value_changed, double)
+
public:
void set_value(double p_val);
void set_min(double p_min);
diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp
index e2a0d568a1..ed79da5c22 100644
--- a/scene/gui/reference_rect.cpp
+++ b/scene/gui/reference_rect.cpp
@@ -33,13 +33,15 @@
#include "core/config/engine.h"
void ReferenceRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (!is_inside_tree()) {
- return;
- }
- if (Engine::get_singleton()->is_editor_hint() || !editor_only) {
- draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (!is_inside_tree()) {
+ return;
+ }
+ if (Engine::get_singleton()->is_editor_hint() || !editor_only) {
+ draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width);
+ }
+ } break;
}
}
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 151ae2f092..0a36176c98 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -33,6 +33,7 @@
#include "core/math/math_defs.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
+#include "label.h"
#include "scene/scene_string_names.h"
#include "servers/display_server.h"
@@ -205,6 +206,49 @@ String RichTextLabel::_letters(int p_num, bool p_capitalize) const {
return s;
}
+void RichTextLabel::_update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size) {
+ ERR_FAIL_COND(p_frame == nullptr);
+ ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
+
+ Line &l = p_frame->lines.write[p_line];
+
+ RID t = l.text_buf->get_rid();
+ int spans = TS->shaped_get_span_count(t);
+ for (int i = 0; i < spans; i++) {
+ ItemText *it = (ItemText *)(uint64_t)TS->shaped_get_span_meta(t, i);
+ if (it) {
+ Ref<Font> font = _find_font(it);
+ if (font.is_null()) {
+ font = p_base_font;
+ }
+ int font_size = _find_font_size(it);
+ if (font_size == -1) {
+ font_size = p_base_font_size;
+ }
+ Dictionary font_ftr = _find_font_features(it);
+ TS->shaped_set_span_update_font(t, i, font->get_rids(), font_size, font_ftr);
+ }
+ }
+
+ Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
+ for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
+ switch (it->type) {
+ case ITEM_TABLE: {
+ ItemTable *table = static_cast<ItemTable *>(it);
+ for (Item *E : table->subitems) {
+ ERR_CONTINUE(E->type != ITEM_FRAME); // Children should all be frames.
+ ItemFrame *frame = static_cast<ItemFrame *>(E);
+ for (int i = 0; i < frame->lines.size(); i++) {
+ _update_line_font(frame, i, p_base_font, p_base_font_size);
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+ }
+}
+
void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width) {
ERR_FAIL_COND(p_frame == nullptr);
ERR_FAIL_COND(p_line < 0 || p_line >= p_frame->lines.size());
@@ -378,9 +422,24 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Line &l = p_frame->lines.write[p_line];
+ uint16_t autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case AUTOWRAP_OFF:
+ break;
+ }
+
// Clear cache.
l.text_buf->clear();
- l.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
+ l.text_buf->set_flags(autowrap_flags | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_TRIM_EDGE_SPACES);
l.char_offset = *r_char_offset;
l.char_count = 0;
@@ -449,13 +508,13 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
remaining_characters -= tx.length();
- l.text_buf->add_string(tx, font, font_size, font_ftr, lang);
+ l.text_buf->add_string(tx, font, font_size, font_ftr, lang, (uint64_t)it);
text += tx;
l.char_count += tx.length();
} break;
case ITEM_IMAGE: {
ItemImage *img = (ItemImage *)it;
- l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1);
+ l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1);
text += String::chr(0xfffc);
l.char_count++;
remaining_characters--;
@@ -843,9 +902,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
for (int i = 0; i < gl_size; i++) {
Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start);
int size = _find_outline_size(it, p_outline_size);
- Color font_color = _find_outline_color(it, p_outline_color);
+ Color font_color = _find_color(it, p_base_color);
+ Color font_outline_color = _find_outline_color(it, p_outline_color);
Color font_shadow_color = p_font_shadow_color;
- if ((size <= 0 || font_color.a == 0) && (font_shadow_color.a == 0)) {
+ if ((size <= 0 || font_outline_color.a == 0) && (font_shadow_color.a == 0)) {
gloff.x += glyphs[i].advance;
continue;
}
@@ -891,11 +951,11 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
faded_visibility -= (float)(glyphs[i].start - fade->starting_index) / (float)fade->length;
faded_visibility = faded_visibility < 0.0f ? 0.0f : faded_visibility;
}
- font_color.a = faded_visibility;
+ font_outline_color.a = faded_visibility;
font_shadow_color.a = faded_visibility;
}
- bool visible = (font_color.a != 0) || (font_shadow_color.a != 0);
+ bool visible = (font_outline_color.a != 0) || (font_shadow_color.a != 0);
for (int j = 0; j < fx_stack.size(); j++) {
ItemFX *item_fx = fx_stack[j];
@@ -965,18 +1025,20 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
// Draw glyph outlines.
+ const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a);
+ const Color modulated_shadow_color = font_shadow_color * Color(1, 1, 1, font_color.a);
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs));
if (!skip && frid != RID()) {
- if (font_shadow_color.a > 0) {
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ if (modulated_shadow_color.a > 0) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, modulated_shadow_color);
}
- if (font_shadow_color.a > 0 && p_shadow_outline_size > 0) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, font_shadow_color);
+ if (modulated_shadow_color.a > 0 && p_shadow_outline_size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, p_shadow_outline_size, p_ofs + fx_offset + gloff + p_shadow_ofs, gl, modulated_shadow_color);
}
- if (font_color.a != 0.0 && size > 0) {
- TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
+ if (modulated_outline_color.a != 0.0 && size > 0) {
+ TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, modulated_outline_color);
}
}
processed_glyphs_ol++;
@@ -1443,12 +1505,17 @@ void RichTextLabel::_notification(int p_what) {
update();
}
} break;
+
case NOTIFICATION_RESIZED: {
main->first_resized_line = 0; //invalidate ALL
update();
+ } break;
+ case NOTIFICATION_THEME_CHANGED: {
+ main->first_invalid_font_line = 0; //invalidate ALL
+ update();
} break;
- case NOTIFICATION_THEME_CHANGED:
+
case NOTIFICATION_ENTER_TREE: {
if (!text.is_empty()) {
set_text(text);
@@ -1457,11 +1524,13 @@ void RichTextLabel::_notification(int p_what) {
main->first_invalid_line = 0; //invalidate ALL
update();
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
main->first_invalid_line = 0; //invalidate ALL
update();
} break;
+
case NOTIFICATION_DRAW: {
_validate_line_caches(main);
_update_scroll();
@@ -1516,6 +1585,7 @@ void RichTextLabel::_notification(int p_what) {
from_line++;
}
} break;
+
case NOTIFICATION_INTERNAL_PROCESS: {
if (is_visible_in_tree()) {
double dt = get_process_delta_time();
@@ -1523,12 +1593,17 @@ void RichTextLabel::_notification(int p_what) {
update();
}
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (deselect_on_focus_loss_enabled) {
selection.active = false;
update();
}
} break;
+
+ case NOTIFICATION_DRAG_END: {
+ selection.drag_attempt = false;
+ } break;
}
}
@@ -1545,6 +1620,10 @@ Control::CursorShape RichTextLabel::get_cursor_shape(const Point2 &p_pos) const
return get_default_cursor_shape(); //invalid
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return get_default_cursor_shape(); //invalid
+ }
+
if (main->first_resized_line < main->lines.size()) {
return get_default_cursor_shape(); //invalid
}
@@ -1569,6 +1648,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -1582,6 +1664,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
int c_index = 0;
bool outside;
+ selection.drag_attempt = false;
+
_find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
if (c_item != nullptr) {
if (selection.enabled) {
@@ -1592,17 +1676,22 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
// Erase previous selection.
if (selection.active) {
- selection.from_frame = nullptr;
- selection.from_line = 0;
- selection.from_item = nullptr;
- selection.from_char = 0;
- selection.to_frame = nullptr;
- selection.to_line = 0;
- selection.to_item = nullptr;
- selection.to_char = 0;
- selection.active = false;
-
- update();
+ if (_is_click_inside_selection()) {
+ selection.drag_attempt = true;
+ selection.click_item = nullptr;
+ } else {
+ selection.from_frame = nullptr;
+ selection.from_line = 0;
+ selection.from_item = nullptr;
+ selection.from_char = 0;
+ selection.to_frame = nullptr;
+ selection.to_line = 0;
+ selection.to_item = nullptr;
+ selection.to_char = 0;
+ selection.active = false;
+
+ update();
+ }
}
}
}
@@ -1615,6 +1704,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
int c_index = 0;
bool outside;
+ selection.drag_attempt = false;
+
_find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside);
if (c_frame) {
@@ -1646,6 +1737,22 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
selection.click_item = nullptr;
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (_is_click_inside_selection()) {
+ selection.from_frame = nullptr;
+ selection.from_line = 0;
+ selection.from_item = nullptr;
+ selection.from_char = 0;
+ selection.to_frame = nullptr;
+ selection.to_line = 0;
+ selection.to_item = nullptr;
+ selection.to_char = 0;
+ selection.active = false;
+
+ update();
+ }
+ }
if (!b->is_double_click() && !scroll_updated) {
Item *c_item = nullptr;
@@ -1732,6 +1839,9 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
if (main->first_invalid_line < main->lines.size()) {
return;
}
+ if (main->first_invalid_font_line < main->lines.size()) {
+ return;
+ }
if (main->first_resized_line < main->lines.size()) {
return;
}
@@ -2184,6 +2294,18 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
if (p_frame->first_invalid_line == p_frame->lines.size()) {
+ Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
+ int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
+
+ // Update fonts.
+ if (p_frame->first_invalid_font_line != p_frame->lines.size()) {
+ for (int i = p_frame->first_invalid_font_line; i < p_frame->lines.size(); i++) {
+ _update_line_font(p_frame, i, base_font, base_font_size);
+ }
+ p_frame->first_resized_line = p_frame->first_invalid_font_line;
+ p_frame->first_invalid_font_line = p_frame->lines.size();
+ }
+
if (p_frame->first_resized_line == p_frame->lines.size()) {
return;
}
@@ -2191,9 +2313,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
// Resize lines without reshaping.
Rect2 text_rect = _get_text_rect();
- Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
- int base_font_size = get_theme_font_size(SNAME("normal_font_size"));
-
for (int i = p_frame->first_resized_line; i < p_frame->lines.size(); i++) {
_resize_line(p_frame, i, base_font, base_font_size, text_rect.get_size().width - scroll_w);
}
@@ -2237,6 +2356,7 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
p_frame->first_invalid_line = p_frame->lines.size();
p_frame->first_resized_line = p_frame->lines.size();
+ p_frame->first_invalid_font_line = p_frame->lines.size();
updating_scroll = true;
vscroll->set_max(total_height);
@@ -3133,6 +3253,10 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_paragraph(HORIZONTAL_ALIGNMENT_FILL);
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "left") {
+ push_paragraph(HORIZONTAL_ALIGNMENT_LEFT);
+ pos = brk_end + 1;
+ tag_stack.push_front(tag);
} else if (tag == "right") {
push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT);
pos = brk_end + 1;
@@ -3608,7 +3732,7 @@ void RichTextLabel::scroll_to_line(int p_line) {
if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) {
float line_offset = 0.f;
for (int j = 0; j < p_line - line_count; j++) {
- line_offset += main->lines[i].text_buf->get_line_size(j).y;
+ line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation"));
}
vscroll->set_value(main->lines[i].offset.y + line_offset);
return;
@@ -3617,6 +3741,28 @@ void RichTextLabel::scroll_to_line(int p_line) {
}
}
+float RichTextLabel::get_line_offset(int p_line) {
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) {
+ float line_offset = 0.f;
+ for (int j = 0; j < p_line - line_count; j++) {
+ line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation"));
+ }
+ return main->lines[i].offset.y + line_offset;
+ }
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
+ return 0;
+}
+
+float RichTextLabel::get_paragraph_offset(int p_paragraph) {
+ if (0 <= p_paragraph && p_paragraph < main->lines.size()) {
+ return main->lines[p_paragraph].offset.y;
+ }
+ return 0;
+}
+
int RichTextLabel::get_line_count() const {
int line_count = 0;
for (int i = 0; i < main->lines.size(); i++) {
@@ -3653,6 +3799,29 @@ void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) {
}
}
+Variant RichTextLabel::get_drag_data(const Point2 &p_point) {
+ if (selection.drag_attempt && selection.enabled) {
+ String t = get_selected_text();
+ Label *l = memnew(Label);
+ l->set_text(t);
+ set_drag_preview(l);
+ return t;
+ }
+
+ return Variant();
+}
+
+bool RichTextLabel::_is_click_inside_selection() const {
+ if (selection.active && selection.enabled && selection.click_frame && selection.from_frame && selection.to_frame) {
+ const Line &l_click = selection.click_frame->lines[selection.click_line];
+ const Line &l_from = selection.from_frame->lines[selection.from_line];
+ const Line &l_to = selection.to_frame->lines[selection.to_line];
+ return (l_click.char_offset + selection.click_char >= l_from.char_offset + selection.from_char) && (l_click.char_offset + selection.click_char <= l_to.char_offset + selection.to_char);
+ } else {
+ return false;
+ }
+}
+
bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) {
List<Item *>::Element *E = p_from;
while (E != nullptr) {
@@ -3911,7 +4080,7 @@ int RichTextLabel::get_selection_to() const {
void RichTextLabel::set_text(const String &p_bbcode) {
text = p_bbcode;
- if (is_inside_tree() && use_bbcode) {
+ if (use_bbcode) {
parse_bbcode(p_bbcode);
} else { // raw text
clear();
@@ -4011,6 +4180,19 @@ String RichTextLabel::get_language() const {
return language;
}
+void RichTextLabel::set_autowrap_mode(RichTextLabel::AutowrapMode p_mode) {
+ if (autowrap_mode != p_mode) {
+ autowrap_mode = p_mode;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
+}
+
+RichTextLabel::AutowrapMode RichTextLabel::get_autowrap_mode() const {
+ return autowrap_mode;
+}
+
void RichTextLabel::set_percent_visible(float p_percent) {
if (percent_visible != p_percent) {
if (p_percent < 0 || p_percent >= 1) {
@@ -4063,6 +4245,14 @@ int RichTextLabel::get_content_height() const {
return total_height;
}
+int RichTextLabel::get_content_width() const {
+ int total_width = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x);
+ }
+ return total_width;
+}
+
#ifndef DISABLE_DEPRECATED
// People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0)
// Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty.
@@ -4122,6 +4312,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_language", "language"), &RichTextLabel::set_language);
ClassDB::bind_method(D_METHOD("get_language"), &RichTextLabel::get_language);
+ ClassDB::bind_method(D_METHOD("set_autowrap_mode", "autowrap_mode"), &RichTextLabel::set_autowrap_mode);
+ ClassDB::bind_method(D_METHOD("get_autowrap_mode"), &RichTextLabel::get_autowrap_mode);
+
ClassDB::bind_method(D_METHOD("set_meta_underline", "enable"), &RichTextLabel::set_meta_underline);
ClassDB::bind_method(D_METHOD("is_meta_underlined"), &RichTextLabel::is_meta_underlined);
@@ -4170,6 +4363,8 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible);
ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible);
+ ClassDB::bind_method(D_METHOD("get_character_line", "character"), &RichTextLabel::get_character_line);
+ ClassDB::bind_method(D_METHOD("get_character_paragraph", "character"), &RichTextLabel::get_character_paragraph);
ClassDB::bind_method(D_METHOD("get_total_character_count"), &RichTextLabel::get_total_character_count);
ClassDB::bind_method(D_METHOD("set_use_bbcode", "enable"), &RichTextLabel::set_use_bbcode);
@@ -4182,6 +4377,10 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count);
ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height);
+ ClassDB::bind_method(D_METHOD("get_content_width"), &RichTextLabel::get_content_width);
+
+ ClassDB::bind_method(D_METHOD("get_line_offset", "line"), &RichTextLabel::get_line_offset);
+ ClassDB::bind_method(D_METHOD("get_paragraph_offset", "paragraph"), &RichTextLabel::get_paragraph_offset);
ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values);
@@ -4189,28 +4388,27 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects);
ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
- ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
-
- ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
+ // Note: set "bbcode_enabled" first, to avoid unnecessery "text" resets.
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color");
-
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled");
-
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode");
+ // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied.
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior");
+ ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible");
+
+ ADD_GROUP("Locale", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
@@ -4222,6 +4420,11 @@ void RichTextLabel::_bind_methods() {
ADD_SIGNAL(MethodInfo("meta_hover_started", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("meta_hover_ended", PropertyInfo(Variant::NIL, "meta", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
+ BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
+ BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
+ BIND_ENUM_CONSTANT(AUTOWRAP_WORD_SMART);
+
BIND_ENUM_CONSTANT(LIST_NUMBERS);
BIND_ENUM_CONSTANT(LIST_LETTERS);
BIND_ENUM_CONSTANT(LIST_ROMAN);
@@ -4297,6 +4500,36 @@ int RichTextLabel::get_visible_characters() const {
return visible_characters;
}
+int RichTextLabel::get_character_line(int p_char) {
+ int line_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
+ for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) {
+ Vector2i range = main->lines[i].text_buf->get_line_range(j);
+ if (main->lines[i].char_offset + range.x < p_char && p_char <= main->lines[i].char_offset + range.y) {
+ return line_count;
+ }
+ line_count++;
+ }
+ } else {
+ line_count += main->lines[i].text_buf->get_line_count();
+ }
+ }
+ return -1;
+}
+
+int RichTextLabel::get_character_paragraph(int p_char) {
+ int para_count = 0;
+ for (int i = 0; i < main->lines.size(); i++) {
+ if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) {
+ return para_count;
+ } else {
+ para_count++;
+ }
+ }
+ return -1;
+}
+
int RichTextLabel::get_total_character_count() const {
// Note: Do not use line buffer "char_count", it includes only visible characters.
int tc = 0;
@@ -4486,7 +4719,7 @@ Dictionary RichTextLabel::parse_expressions_for_values(Vector<String> p_expressi
return d;
}
-RichTextLabel::RichTextLabel() {
+RichTextLabel::RichTextLabel(const String &p_text) {
main = memnew(ItemFrame);
main->index = 0;
current = main;
@@ -4494,6 +4727,7 @@ RichTextLabel::RichTextLabel() {
main->lines.write[0].from = main;
main->first_invalid_line = 0;
main->first_resized_line = 0;
+ main->first_invalid_font_line = 0;
current_frame = main;
vscroll = memnew(VScrollBar);
@@ -4507,6 +4741,8 @@ RichTextLabel::RichTextLabel() {
vscroll->set_step(1);
vscroll->hide();
+ set_text(p_text);
+
set_clip_contents(true);
}
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 70467e7e7c..076b68a0da 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -39,6 +39,13 @@ class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
public:
+ enum AutowrapMode {
+ AUTOWRAP_OFF,
+ AUTOWRAP_ARBITRARY,
+ AUTOWRAP_WORD,
+ AUTOWRAP_WORD_SMART
+ };
+
enum ListType {
LIST_NUMBERS,
LIST_LETTERS,
@@ -129,6 +136,7 @@ private:
Vector<Line> lines;
int first_invalid_line = 0;
+ int first_invalid_font_line = 0;
int first_resized_line = 0;
ItemFrame *parent_frame = nullptr;
@@ -345,6 +353,8 @@ private:
VScrollBar *vscroll = nullptr;
+ AutowrapMode autowrap_mode = AUTOWRAP_WORD_SMART;
+
bool scroll_visible = false;
bool scroll_follow = false;
bool scroll_following = false;
@@ -397,6 +407,7 @@ private:
bool active = false; // anything selected? i.e. from, to, etc. valid?
bool enabled = false; // allow selections?
+ bool drag_attempt = false;
};
Selection selection;
@@ -406,6 +417,7 @@ private:
float percent_visible = 1.0;
VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
+ bool _is_click_inside_selection() const;
void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr);
String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const;
@@ -414,6 +426,7 @@ private:
void _shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width, int *r_char_offset);
void _resize_line(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size, int p_width);
+ void _update_line_font(ItemFrame *p_frame, int p_line, const Ref<Font> &p_base_font, int p_base_font_size);
int _draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Color &p_base_color, int p_outline_size, const Color &p_outline_color, const Color &p_font_shadow_color, int p_shadow_outline_size, const Point2 &p_shadow_ofs, int &r_processed_glyphs);
float _find_click_in_line(ItemFrame *p_frame, int p_line, const Vector2 &p_ofs, int p_width, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr);
@@ -538,15 +551,20 @@ public:
int get_paragraph_count() const;
int get_visible_paragraph_count() const;
+ float get_line_offset(int p_line);
+ float get_paragraph_offset(int p_paragraph);
+
void scroll_to_line(int p_line);
int get_line_count() const;
int get_visible_line_count() const;
int get_content_height() const;
+ int get_content_width() const;
VScrollBar *get_v_scroll_bar() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
+ virtual Variant get_drag_data(const Point2 &p_point) override;
void set_selection_enabled(bool p_enabled);
bool is_selection_enabled() const;
@@ -572,6 +590,9 @@ public:
void set_language(const String &p_language);
String get_language() const;
+ void set_autowrap_mode(AutowrapMode p_mode);
+ AutowrapMode get_autowrap_mode() const;
+
void set_structured_text_bidi_override(Control::StructuredTextParser p_parser);
Control::StructuredTextParser get_structured_text_bidi_override() const;
@@ -580,6 +601,8 @@ public:
void set_visible_characters(int p_visible);
int get_visible_characters() const;
+ int get_character_line(int p_char);
+ int get_character_paragraph(int p_char);
int get_total_character_count() const;
int get_total_glyph_count() const;
@@ -597,10 +620,11 @@ public:
void set_fixed_size_to_width(int p_width);
virtual Size2 get_minimum_size() const override;
- RichTextLabel();
+ RichTextLabel(const String &p_text = String());
~RichTextLabel();
};
+VARIANT_ENUM_CAST(RichTextLabel::AutowrapMode);
VARIANT_ENUM_CAST(RichTextLabel::ListType);
VARIANT_ENUM_CAST(RichTextLabel::ItemType);
VARIANT_ENUM_CAST(RichTextLabel::VisibleCharactersBehavior);
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 343056957c..e1b0e8cca8 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -218,195 +218,198 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) {
}
void ScrollBar::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- RID ci = get_canvas_item();
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ RID ci = get_canvas_item();
- Ref<Texture2D> decr, incr;
+ Ref<Texture2D> decr, incr;
- if (decr_active) {
- decr = get_theme_icon(SNAME("decrement_pressed"));
- } else if (highlight == HIGHLIGHT_DECR) {
- decr = get_theme_icon(SNAME("decrement_highlight"));
- } else {
- decr = get_theme_icon(SNAME("decrement"));
- }
+ if (decr_active) {
+ decr = get_theme_icon(SNAME("decrement_pressed"));
+ } else if (highlight == HIGHLIGHT_DECR) {
+ decr = get_theme_icon(SNAME("decrement_highlight"));
+ } else {
+ decr = get_theme_icon(SNAME("decrement"));
+ }
- if (incr_active) {
- incr = get_theme_icon(SNAME("increment_pressed"));
- } else if (highlight == HIGHLIGHT_INCR) {
- incr = get_theme_icon(SNAME("increment_highlight"));
- } else {
- incr = get_theme_icon(SNAME("increment"));
- }
+ if (incr_active) {
+ incr = get_theme_icon(SNAME("increment_pressed"));
+ } else if (highlight == HIGHLIGHT_INCR) {
+ incr = get_theme_icon(SNAME("increment_highlight"));
+ } else {
+ incr = get_theme_icon(SNAME("increment"));
+ }
- Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll"));
+ Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll"));
- Ref<StyleBox> grabber;
- if (drag.active) {
- grabber = get_theme_stylebox(SNAME("grabber_pressed"));
- } else if (highlight == HIGHLIGHT_RANGE) {
- grabber = get_theme_stylebox(SNAME("grabber_highlight"));
- } else {
- grabber = get_theme_stylebox(SNAME("grabber"));
- }
+ Ref<StyleBox> grabber;
+ if (drag.active) {
+ grabber = get_theme_stylebox(SNAME("grabber_pressed"));
+ } else if (highlight == HIGHLIGHT_RANGE) {
+ grabber = get_theme_stylebox(SNAME("grabber_highlight"));
+ } else {
+ grabber = get_theme_stylebox(SNAME("grabber"));
+ }
- Point2 ofs;
+ Point2 ofs;
- decr->draw(ci, Point2());
+ decr->draw(ci, Point2());
- if (orientation == HORIZONTAL) {
- ofs.x += decr->get_width();
- } else {
- ofs.y += decr->get_height();
- }
+ if (orientation == HORIZONTAL) {
+ ofs.x += decr->get_width();
+ } else {
+ ofs.y += decr->get_height();
+ }
- Size2 area = get_size();
+ Size2 area = get_size();
- if (orientation == HORIZONTAL) {
- area.width -= incr->get_width() + decr->get_width();
- } else {
- area.height -= incr->get_height() + decr->get_height();
- }
+ if (orientation == HORIZONTAL) {
+ area.width -= incr->get_width() + decr->get_width();
+ } else {
+ area.height -= incr->get_height() + decr->get_height();
+ }
- bg->draw(ci, Rect2(ofs, area));
+ bg->draw(ci, Rect2(ofs, area));
- if (orientation == HORIZONTAL) {
- ofs.width += area.width;
- } else {
- ofs.height += area.height;
- }
+ if (orientation == HORIZONTAL) {
+ ofs.width += area.width;
+ } else {
+ ofs.height += area.height;
+ }
- incr->draw(ci, ofs);
- Rect2 grabber_rect;
+ incr->draw(ci, ofs);
+ Rect2 grabber_rect;
- if (orientation == HORIZONTAL) {
- grabber_rect.size.width = get_grabber_size();
- grabber_rect.size.height = get_size().height;
- grabber_rect.position.y = 0;
- grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT);
- } else {
- grabber_rect.size.width = get_size().width;
- grabber_rect.size.height = get_grabber_size();
- grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP);
- grabber_rect.position.x = 0;
- }
+ if (orientation == HORIZONTAL) {
+ grabber_rect.size.width = get_grabber_size();
+ grabber_rect.size.height = get_size().height;
+ grabber_rect.position.y = 0;
+ grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT);
+ } else {
+ grabber_rect.size.width = get_size().width;
+ grabber_rect.size.height = get_grabber_size();
+ grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP);
+ grabber_rect.position.x = 0;
+ }
- grabber->draw(ci, grabber_rect);
- }
+ grabber->draw(ci, grabber_rect);
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE) {
- if (has_node(drag_node_path)) {
- Node *n = get_node(drag_node_path);
- drag_node = Object::cast_to<Control>(n);
- }
+ case NOTIFICATION_ENTER_TREE: {
+ if (has_node(drag_node_path)) {
+ Node *n = get_node(drag_node_path);
+ drag_node = Object::cast_to<Control>(n);
+ }
- if (drag_node) {
- drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
- drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT);
- }
- }
- if (p_what == NOTIFICATION_EXIT_TREE) {
- if (drag_node) {
- drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
- drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit));
- }
+ if (drag_node) {
+ drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
+ drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT);
+ }
+ } break;
- drag_node = nullptr;
- }
+ case NOTIFICATION_EXIT_TREE: {
+ if (drag_node) {
+ drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input));
+ drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit));
+ }
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (scrolling) {
- if (get_value() != target_scroll) {
- double target = target_scroll - get_value();
- double dist = sqrt(target * target);
- double vel = ((target / dist) * 500) * get_physics_process_delta_time();
+ drag_node = nullptr;
+ } break;
- if (Math::abs(vel) >= dist) {
- set_value(target_scroll);
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (scrolling) {
+ if (get_value() != target_scroll) {
+ double target = target_scroll - get_value();
+ double dist = sqrt(target * target);
+ double vel = ((target / dist) * 500) * get_physics_process_delta_time();
+
+ if (Math::abs(vel) >= dist) {
+ set_value(target_scroll);
+ scrolling = false;
+ set_physics_process_internal(false);
+ } else {
+ set_value(get_value() + vel);
+ }
+ } else {
scrolling = false;
set_physics_process_internal(false);
- } else {
- set_value(get_value() + vel);
}
- } else {
- scrolling = false;
- set_physics_process_internal(false);
- }
- } else if (drag_node_touching) {
- if (drag_node_touching_deaccel) {
- Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
- pos += drag_node_speed * get_physics_process_delta_time();
+ } else if (drag_node_touching) {
+ if (drag_node_touching_deaccel) {
+ Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0);
+ pos += drag_node_speed * get_physics_process_delta_time();
- bool turnoff = false;
+ bool turnoff = false;
- if (orientation == HORIZONTAL) {
- if (pos.x < 0) {
- pos.x = 0;
- turnoff = true;
- }
+ if (orientation == HORIZONTAL) {
+ if (pos.x < 0) {
+ pos.x = 0;
+ turnoff = true;
+ }
- if (pos.x > (get_max() - get_page())) {
- pos.x = get_max() - get_page();
- turnoff = true;
- }
+ if (pos.x > (get_max() - get_page())) {
+ pos.x = get_max() - get_page();
+ turnoff = true;
+ }
- set_value(pos.x);
+ set_value(pos.x);
- float sgn_x = drag_node_speed.x < 0 ? -1 : 1;
- float val_x = Math::abs(drag_node_speed.x);
- val_x -= 1000 * get_physics_process_delta_time();
+ float sgn_x = drag_node_speed.x < 0 ? -1 : 1;
+ float val_x = Math::abs(drag_node_speed.x);
+ val_x -= 1000 * get_physics_process_delta_time();
- if (val_x < 0) {
- turnoff = true;
- }
+ if (val_x < 0) {
+ turnoff = true;
+ }
- drag_node_speed.x = sgn_x * val_x;
+ drag_node_speed.x = sgn_x * val_x;
- } else {
- if (pos.y < 0) {
- pos.y = 0;
- turnoff = true;
- }
+ } else {
+ if (pos.y < 0) {
+ pos.y = 0;
+ turnoff = true;
+ }
- if (pos.y > (get_max() - get_page())) {
- pos.y = get_max() - get_page();
- turnoff = true;
- }
+ if (pos.y > (get_max() - get_page())) {
+ pos.y = get_max() - get_page();
+ turnoff = true;
+ }
- set_value(pos.y);
+ set_value(pos.y);
- float sgn_y = drag_node_speed.y < 0 ? -1 : 1;
- float val_y = Math::abs(drag_node_speed.y);
- val_y -= 1000 * get_physics_process_delta_time();
+ float sgn_y = drag_node_speed.y < 0 ? -1 : 1;
+ float val_y = Math::abs(drag_node_speed.y);
+ val_y -= 1000 * get_physics_process_delta_time();
- if (val_y < 0) {
- turnoff = true;
+ if (val_y < 0) {
+ turnoff = true;
+ }
+ drag_node_speed.y = sgn_y * val_y;
}
- drag_node_speed.y = sgn_y * val_y;
- }
- if (turnoff) {
- set_physics_process_internal(false);
- drag_node_touching = false;
- drag_node_touching_deaccel = false;
- }
+ if (turnoff) {
+ set_physics_process_internal(false);
+ drag_node_touching = false;
+ drag_node_touching_deaccel = false;
+ }
- } else {
- if (time_since_motion == 0 || time_since_motion > 0.1) {
- Vector2 diff = drag_node_accum - last_drag_node_accum;
- last_drag_node_accum = drag_node_accum;
- drag_node_speed = diff / get_physics_process_delta_time();
- }
+ } else {
+ if (time_since_motion == 0 || time_since_motion > 0.1) {
+ Vector2 diff = drag_node_accum - last_drag_node_accum;
+ last_drag_node_accum = drag_node_accum;
+ drag_node_speed = diff / get_physics_process_delta_time();
+ }
- time_since_motion += get_physics_process_delta_time();
+ time_since_motion += get_physics_process_delta_time();
+ }
}
- }
- }
+ } break;
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- highlight = HIGHLIGHT_NONE;
- update();
+ case NOTIFICATION_MOUSE_EXIT: {
+ highlight = HIGHLIGHT_NONE;
+ update();
+ } break;
}
}
@@ -423,11 +426,6 @@ double ScrollBar::get_grabber_size() const {
}
float page = (get_page() > 0) ? get_page() : 0;
- /*
- if (grabber_range < get_step())
- grabber_range=get_step();
- */
-
double area_size = get_area_size();
double grabber_size = page / range * area_size;
return grabber_size + get_grabber_min_size();
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 5e128d594c..b3cf2cbf7e 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -29,6 +29,8 @@
/*************************************************************************/
#include "scroll_container.h"
+
+#include "core/config/project_settings.h"
#include "core/os/os.h"
#include "scene/main/window.h"
@@ -314,97 +316,102 @@ void ScrollContainer::_update_dimensions() {
}
void ScrollContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- _updating_scrollbars = true;
- call_deferred(SNAME("_update_scrollbar_position"));
- };
-
- if (p_what == NOTIFICATION_READY) {
- Viewport *viewport = get_viewport();
- ERR_FAIL_COND(!viewport);
- viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed));
- _update_dimensions();
- }
-
- if (p_what == NOTIFICATION_SORT_CHILDREN) {
- _update_dimensions();
- };
-
- if (p_what == NOTIFICATION_DRAW) {
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
- draw_style_box(sb, Rect2(Vector2(), get_size()));
-
- update_scrollbars();
- }
-
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (drag_touching) {
- if (drag_touching_deaccel) {
- Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value());
- pos += drag_speed * get_physics_process_delta_time();
-
- bool turnoff_h = false;
- bool turnoff_v = false;
-
- if (pos.x < 0) {
- pos.x = 0;
- turnoff_h = true;
- }
- if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) {
- pos.x = h_scroll->get_max() - h_scroll->get_page();
- turnoff_h = true;
- }
-
- if (pos.y < 0) {
- pos.y = 0;
- turnoff_v = true;
- }
- if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) {
- pos.y = v_scroll->get_max() - v_scroll->get_page();
- turnoff_v = true;
- }
-
- if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
- h_scroll->set_value(pos.x);
- }
- if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
- v_scroll->set_value(pos.y);
- }
-
- float sgn_x = drag_speed.x < 0 ? -1 : 1;
- float val_x = Math::abs(drag_speed.x);
- val_x -= 1000 * get_physics_process_delta_time();
-
- if (val_x < 0) {
- turnoff_h = true;
- }
-
- float sgn_y = drag_speed.y < 0 ? -1 : 1;
- float val_y = Math::abs(drag_speed.y);
- val_y -= 1000 * get_physics_process_delta_time();
-
- if (val_y < 0) {
- turnoff_v = true;
- }
-
- drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ _updating_scrollbars = true;
+ call_deferred(SNAME("_update_scrollbar_position"));
+ } break;
+
+ case NOTIFICATION_READY: {
+ Viewport *viewport = get_viewport();
+ ERR_FAIL_COND(!viewport);
+ viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed));
+ _update_dimensions();
+ } break;
+
+ case NOTIFICATION_SORT_CHILDREN: {
+ _update_dimensions();
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
+ draw_style_box(sb, Rect2(Vector2(), get_size()));
+
+ update_scrollbars();
+ } break;
+
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (drag_touching) {
+ if (drag_touching_deaccel) {
+ Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value());
+ pos += drag_speed * get_physics_process_delta_time();
+
+ bool turnoff_h = false;
+ bool turnoff_v = false;
+
+ if (pos.x < 0) {
+ pos.x = 0;
+ turnoff_h = true;
+ }
+ if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) {
+ pos.x = h_scroll->get_max() - h_scroll->get_page();
+ turnoff_h = true;
+ }
+
+ if (pos.y < 0) {
+ pos.y = 0;
+ turnoff_v = true;
+ }
+ if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) {
+ pos.y = v_scroll->get_max() - v_scroll->get_page();
+ turnoff_v = true;
+ }
+
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
+ h_scroll->set_value(pos.x);
+ }
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
+ v_scroll->set_value(pos.y);
+ }
+
+ float sgn_x = drag_speed.x < 0 ? -1 : 1;
+ float val_x = Math::abs(drag_speed.x);
+ val_x -= 1000 * get_physics_process_delta_time();
+
+ if (val_x < 0) {
+ turnoff_h = true;
+ }
+
+ float sgn_y = drag_speed.y < 0 ? -1 : 1;
+ float val_y = Math::abs(drag_speed.y);
+ val_y -= 1000 * get_physics_process_delta_time();
+
+ if (val_y < 0) {
+ turnoff_v = true;
+ }
+
+ drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y);
+
+ if (turnoff_h && turnoff_v) {
+ _cancel_drag();
+ }
- if (turnoff_h && turnoff_v) {
- _cancel_drag();
- }
+ } else {
+ if (time_since_motion == 0 || time_since_motion > 0.1) {
+ Vector2 diff = drag_accum - last_drag_accum;
+ last_drag_accum = drag_accum;
+ drag_speed = diff / get_physics_process_delta_time();
+ }
- } else {
- if (time_since_motion == 0 || time_since_motion > 0.1) {
- Vector2 diff = drag_accum - last_drag_accum;
- last_drag_accum = drag_accum;
- drag_speed = diff / get_physics_process_delta_time();
+ time_since_motion += get_physics_process_delta_time();
}
-
- time_since_motion += get_physics_process_delta_time();
}
- }
+ } break;
}
-};
+}
void ScrollContainer::update_scrollbars() {
Size2 size = get_size();
diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp
index 9c19eb54dc..e3400d9c8f 100644
--- a/scene/gui/separator.cpp
+++ b/scene/gui/separator.cpp
@@ -52,7 +52,6 @@ void Separator::_notification(int p_what) {
} else {
style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y));
}
-
} break;
}
}
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 1d459d589f..4b680f72cf 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "slider.h"
+
#include "core/os/keyboard.h"
Size2 Slider::get_minimum_size() const {
@@ -150,19 +151,23 @@ void Slider::_notification(int p_what) {
update_minimum_size();
update();
} break;
+
case NOTIFICATION_MOUSE_ENTER: {
mouse_inside = true;
update();
} break;
+
case NOTIFICATION_MOUSE_EXIT: {
mouse_inside = false;
update();
} break;
- case NOTIFICATION_VISIBILITY_CHANGED: // fallthrough
+
+ case NOTIFICATION_VISIBILITY_CHANGED:
case NOTIFICATION_EXIT_TREE: {
mouse_inside = false;
grab.active = false;
} break;
+
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2i size = get_size();
@@ -209,7 +214,6 @@ void Slider::_notification(int p_what) {
}
grabber->draw(ci, Point2i(ratio * areasize, size.height / 2 - grabber->get_size().height / 2));
}
-
} break;
}
}
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 19d47ea492..e50d7e765c 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -39,7 +39,7 @@ Size2 SpinBox::get_minimum_size() const {
return ms;
}
-void SpinBox::_value_changed(double) {
+void SpinBox::_value_changed(double p_value) {
String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step())));
if (!prefix.is_empty()) {
value = prefix + " " + value;
@@ -48,6 +48,7 @@ void SpinBox::_value_changed(double) {
value += " " + suffix;
}
line_edit->set_text(value);
+ Range::_value_changed(p_value);
}
void SpinBox::_text_submitted(const String &p_string) {
@@ -196,34 +197,44 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) {
}
void SpinBox::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- Ref<Texture2D> updown = get_theme_icon(SNAME("updown"));
-
- _adjust_width_for_icon(updown);
-
- RID ci = get_canvas_item();
- Size2i size = get_size();
-
- if (is_layout_rtl()) {
- updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2));
- } else {
- updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2));
- }
-
- } else if (p_what == NOTIFICATION_FOCUS_EXIT) {
- //_value_changed(0);
- } else if (p_what == NOTIFICATION_ENTER_TREE) {
- _adjust_width_for_icon(get_theme_icon(SNAME("updown")));
- _value_changed(0);
- } else if (p_what == NOTIFICATION_EXIT_TREE) {
- _release_mouse();
- } else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- _value_changed(0);
- } else if (p_what == NOTIFICATION_THEME_CHANGED) {
- call_deferred(SNAME("update_minimum_size"));
- get_line_edit()->call_deferred(SNAME("update_minimum_size"));
- } else if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- update();
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ Ref<Texture2D> updown = get_theme_icon(SNAME("updown"));
+
+ _adjust_width_for_icon(updown);
+
+ RID ci = get_canvas_item();
+ Size2i size = get_size();
+
+ if (is_layout_rtl()) {
+ updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2));
+ } else {
+ updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2));
+ }
+ } break;
+
+ case NOTIFICATION_ENTER_TREE: {
+ _adjust_width_for_icon(get_theme_icon(SNAME("updown")));
+ _value_changed(0);
+ } break;
+
+ case NOTIFICATION_EXIT_TREE: {
+ _release_mouse();
+ } break;
+
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ _value_changed(0);
+ update();
+ } break;
+
+ case NOTIFICATION_THEME_CHANGED: {
+ call_deferred(SNAME("update_minimum_size"));
+ get_line_edit()->call_deferred(SNAME("update_minimum_size"));
+ } break;
+
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ update();
+ } break;
}
}
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 0691a4b48d..a15e3fe5f5 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -47,7 +47,7 @@ class SpinBox : public Range {
void _release_mouse();
void _text_submitted(const String &p_string);
- virtual void _value_changed(double) override;
+ virtual void _value_changed(double p_value) override;
void _text_changed(const String &p_string);
String prefix;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 874e5868b6..6845d46721 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -168,15 +168,18 @@ void SplitContainer::_notification(int p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
queue_sort();
} break;
+
case NOTIFICATION_SORT_CHILDREN: {
_resort();
} break;
+
case NOTIFICATION_MOUSE_EXIT: {
mouse_inside = false;
if (get_theme_constant(SNAME("autohide"))) {
update();
}
} break;
+
case NOTIFICATION_DRAW: {
if (!_getch(0) || !_getch(1)) {
return;
@@ -200,6 +203,7 @@ void SplitContainer::_notification(int p_what) {
draw_texture(tex, Point2i(middle_sep + (sep - tex->get_width()) / 2, (size.y - tex->get_height()) / 2));
}
} break;
+
case NOTIFICATION_THEME_CHANGED: {
update_minimum_size();
} break;
@@ -336,6 +340,30 @@ bool SplitContainer::is_collapsed() const {
return collapsed;
}
+Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (!vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
+Vector<int> SplitContainer::get_allowed_size_flags_vertical() const {
+ Vector<int> flags;
+ flags.append(SIZE_FILL);
+ if (vertical) {
+ flags.append(SIZE_EXPAND);
+ }
+ flags.append(SIZE_SHRINK_BEGIN);
+ flags.append(SIZE_SHRINK_CENTER);
+ flags.append(SIZE_SHRINK_END);
+ return flags;
+}
+
void SplitContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset);
ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset);
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index ba6fff6f55..a69ffe4de9 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -79,6 +79,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
SplitContainer(bool p_vertical = false);
};
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 760144591e..c66e145bc4 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -91,52 +91,81 @@ int SubViewportContainer::get_stretch_shrink() const {
return shrink;
}
-void SubViewportContainer::_notification(int p_what) {
- if (p_what == NOTIFICATION_RESIZED) {
- if (!stretch) {
- return;
- }
+Vector<int> SubViewportContainer::get_allowed_size_flags_horizontal() const {
+ return Vector<int>();
+}
+
+Vector<int> SubViewportContainer::get_allowed_size_flags_vertical() const {
+ return Vector<int>();
+}
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
+void SubViewportContainer::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_RESIZED: {
+ if (!stretch) {
+ return;
}
- c->set_size(get_size() / shrink);
- }
- }
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
+ c->set_size(get_size() / shrink);
}
-
- if (is_visible_in_tree()) {
- c->set_update_mode(SubViewport::UPDATE_ALWAYS);
- } else {
- c->set_update_mode(SubViewport::UPDATE_DISABLED);
+ } break;
+
+ case NOTIFICATION_ENTER_TREE:
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
+
+ if (is_visible_in_tree()) {
+ c->set_update_mode(SubViewport::UPDATE_ALWAYS);
+ } else {
+ c->set_update_mode(SubViewport::UPDATE_DISABLED);
+ }
+
+ c->set_handle_input_locally(false); //do not handle input locally here
+ }
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
+ }
+
+ if (stretch) {
+ draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size()));
+ } else {
+ draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size()));
+ }
}
+ } break;
- c->set_handle_input_locally(false); //do not handle input locally here
- }
- }
+ case NOTIFICATION_MOUSE_ENTER: {
+ _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER);
+ } break;
- if (p_what == NOTIFICATION_DRAW) {
- for (int i = 0; i < get_child_count(); i++) {
- SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
- if (!c) {
- continue;
- }
+ case NOTIFICATION_MOUSE_EXIT: {
+ _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT);
+ } break;
+ }
+}
- if (stretch) {
- draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size()));
- } else {
- draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size()));
- }
+void SubViewportContainer::_notify_viewports(int p_notification) {
+ for (int i = 0; i < get_child_count(); i++) {
+ SubViewport *c = Object::cast_to<SubViewport>(get_child(i));
+ if (!c) {
+ continue;
}
+ c->notification(p_notification);
}
}
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index e7520763fb..f52f01e4e2 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -38,6 +38,7 @@ class SubViewportContainer : public Container {
bool stretch = false;
int shrink = 1;
+ void _notify_viewports(int p_notification);
protected:
void _notification(int p_what);
@@ -54,6 +55,9 @@ public:
virtual Size2 get_minimum_size() const override;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
SubViewportContainer();
};
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 9da030f0a2..af314b9545 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -32,54 +32,76 @@
#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 TabBar::get_minimum_size() const {
+ Size2 ms;
+
+ if (tabs.is_empty()) {
+ return ms;
+ }
+
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<StyleBox> button_highlight = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> close = get_theme_icon(SNAME("close"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
int y_margin = MAX(MAX(tab_unselected->get_minimum_size().height, tab_selected->get_minimum_size().height), tab_disabled->get_minimum_size().height);
- 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.is_empty()) {
- ms.width += get_theme_constant(SNAME("hseparation"));
- }
+ if (tabs[i].hidden) {
+ continue;
}
- 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);
+ int ofs = ms.width;
+ Ref<StyleBox> style;
if (tabs[i].disabled) {
- ms.width += tab_disabled->get_minimum_size().width;
+ style = tab_disabled;
} else if (current == i) {
- ms.width += tab_selected->get_minimum_size().width;
+ style = tab_selected;
} else {
- ms.width += tab_unselected->get_minimum_size().width;
+ style = tab_unselected;
+ }
+ ms.width += style->get_minimum_size().width;
+
+ Ref<Texture2D> tex = tabs[i].icon;
+ if (tex.is_valid()) {
+ ms.height = MAX(ms.height, tex->get_size().height + y_margin);
+ ms.width += tex->get_size().width + hseparation;
}
+ if (!tabs[i].text.is_empty()) {
+ ms.width += tabs[i].size_text + hseparation;
+ }
+ ms.height = MAX(ms.height, tabs[i].text_buf->get_size().y + y_margin);
+
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current);
+
if (tabs[i].right_button.is_valid()) {
Ref<Texture2D> rb = tabs[i].right_button;
- Size2 bms = rb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+
+ if (close_visible) {
+ ms.width += button_highlight->get_minimum_size().width + rb->get_width();
+ } else {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
+
+ ms.height = MAX(ms.height, rb->get_height() + y_margin);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- Size2 bms = cb->get_size();
- bms.width += get_theme_constant(SNAME("hseparation"));
- ms.width += bms.width;
- ms.height = MAX(bms.height + tab_unselected->get_minimum_size().height, ms.height);
+ if (close_visible) {
+ ms.width += button_highlight->get_margin(SIDE_LEFT) + close->get_width() + hseparation;
+
+ ms.height = MAX(ms.height, close->get_height() + y_margin);
+ }
+
+ if (ms.width - ofs > style->get_minimum_size().width) {
+ ms.width -= hseparation;
}
}
@@ -165,8 +187,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (rb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (rb_hover != -1) {
- // Right mouse button clicked.
- emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
+ emit_signal(SNAME("tab_button_pressed"), rb_hover);
}
rb_pressing = false;
@@ -175,7 +196,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (cb_pressing && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
if (cb_hover != -1) {
- // Close button pressed.
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -184,7 +204,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
}
if (mb->is_pressed() && (mb->get_button_index() == MouseButton::LEFT || (select_with_rmb && mb->get_button_index() == MouseButton::RIGHT))) {
- // Clicks.
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -234,6 +253,10 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
int found = -1;
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_pressing = true;
update();
@@ -256,6 +279,12 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (found != -1) {
set_current_tab(found);
+
+ if (mb->get_button_index() == MouseButton::RIGHT) {
+ // Right mouse button clicked.
+ emit_signal(SNAME("tab_rmb_clicked"), found);
+ }
+
emit_signal(SNAME("tab_clicked"), found);
}
}
@@ -275,29 +304,35 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
- 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.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, font, font_size, tabs[p_tab].opentype_features, !tabs[p_tab].language.is_empty() ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
}
void TabBar::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- _update_cache();
update();
} break;
+
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED: {
for (int i = 0; i < tabs.size(); ++i) {
_shape(i);
}
- _update_cache();
- update_minimum_size();
- update();
- } break;
+
+ [[fallthrough]];
+ }
case NOTIFICATION_RESIZED: {
+ int ofs_old = offset;
+ int max_old = max_drawn_tab;
+
_update_cache();
_ensure_no_over_offset();
- ensure_tab_visible(current);
+
+ if (scroll_to_selected && (offset != ofs_old || max_drawn_tab != max_old)) {
+ ensure_tab_visible(current);
+ }
} break;
+
case NOTIFICATION_DRAW: {
if (tabs.is_empty()) {
return;
@@ -322,6 +357,10 @@ void TabBar::_notification(int p_what) {
// Draw unselected tabs in the back.
for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (i != current) {
Ref<StyleBox> sb;
Color col;
@@ -344,14 +383,14 @@ void TabBar::_notification(int p_what) {
}
// Draw selected tab in the front, but only if it's visible.
- if (current >= offset && current <= max_drawn_tab) {
+ if (current >= offset && current <= max_drawn_tab && !tabs[current].hidden) {
Ref<StyleBox> sb = tabs[current].disabled ? tab_disabled : tab_selected;
float x = rtl ? size.width - tabs[current].ofs_cache - tabs[current].size_cache : tabs[current].ofs_cache;
_draw_tab(sb, font_selected_color, current, x);
}
- if (offset > 0 || missing_right) {
+ if (buttons_visible) {
int vofs = (get_size().height - incr->get_size().height) / 2;
if (rtl) {
@@ -386,47 +425,52 @@ void TabBar::_notification(int p_what) {
void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
RID ci = get_canvas_item();
+ bool rtl = is_layout_rtl();
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
+ int hseparation = get_theme_constant(SNAME("hseparation"));
- Tab tab = tabs[p_index];
-
- Rect2 sb_rect = Rect2(p_x, 0, tab.size_cache, get_size().height);
+ Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height);
p_tab_style->draw(ci, sb_rect);
- p_x += p_tab_style->get_margin(SIDE_LEFT);
+ p_x += rtl ? tabs[p_index].size_cache - p_tab_style->get_margin(SIDE_LEFT) : p_tab_style->get_margin(SIDE_LEFT);
Size2i sb_ms = p_tab_style->get_minimum_size();
- Ref<Texture2D> icon = tab.icon;
+ // Draw the icon.
+ Ref<Texture2D> icon = tabs[p_index].icon;
if (icon.is_valid()) {
- icon->draw(ci, Point2i(p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
+ icon->draw(ci, Point2i(rtl ? p_x - icon->get_width() : p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- if (!tab.text.is_empty()) {
- p_x += icon->get_width() + get_theme_constant(SNAME("hseparation"));
- }
+ p_x = rtl ? p_x - icon->get_width() - hseparation : p_x + icon->get_width() + hseparation;
}
- Vector2 text_pos = Point2i(p_x, p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tab.text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tab.text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tab.text_buf->draw(ci, text_pos, p_font_color);
+ // Draw the text.
+ if (!tabs[p_index].text.is_empty()) {
+ Point2i text_pos = Point2i(rtl ? p_x - tabs[p_index].size_text : p_x,
+ p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[p_index].text_buf->get_size().y) / 2);
- p_x += tab.size_text;
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ tabs[p_index].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ tabs[p_index].text_buf->draw(ci, text_pos, p_font_color);
- if (tab.right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> rb = tab.right_button;
+ p_x = rtl ? p_x - tabs[p_index].size_text - hseparation : p_x + tabs[p_index].size_text + hseparation;
+ }
- p_x += get_theme_constant(SNAME("hseparation"));
+ // Draw and calculate rect of the right button.
+ if (tabs[p_index].right_button.is_valid()) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
+ Ref<Texture2D> rb = tabs[p_index].right_button;
Rect2 rb_rect;
rb_rect.size = style->get_minimum_size() + rb->get_size();
- rb_rect.position.x = p_x;
+ rb_rect.position.x = rtl ? p_x - rb_rect.size.width : p_x;
rb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
+ tabs.write[p_index].rb_rect = rb_rect;
+
if (rb_hover == p_index) {
if (rb_pressing) {
get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
@@ -435,41 +479,62 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in
}
}
- rb->draw(ci, Point2i(p_x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- p_x += rb->get_width();
- tabs.write[p_index].rb_rect = rb_rect;
+ rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
+
+ p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width;
}
+ // Draw and calculate rect of the close button.
if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_index == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- p_x += get_theme_constant(SNAME("hseparation"));
-
Rect2 cb_rect;
cb_rect.size = style->get_minimum_size() + cb->get_size();
- cb_rect.position.x = p_x;
+ cb_rect.position.x = rtl ? p_x - cb_rect.size.width : p_x;
cb_rect.position.y = p_tab_style->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
- if (!tab.disabled && cb_hover == p_index) {
+ tabs.write[p_index].cb_rect = cb_rect;
+
+ if (!tabs[p_index].disabled && cb_hover == p_index) {
if (cb_pressing) {
- get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, cb_rect);
} else {
style->draw(ci, cb_rect);
}
}
- cb->draw(ci, Point2i(p_x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- p_x += cb->get_width();
- tabs.write[p_index].cb_rect = cb_rect;
+ cb->draw(ci, Point2i(cb_rect.position.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
}
}
void TabBar::set_tab_count(int p_count) {
+ if (p_count == tabs.size()) {
+ return;
+ }
+
ERR_FAIL_COND(p_count < 0);
tabs.resize(p_count);
- _update_cache();
+
+ if (p_count == 0) {
+ offset = 0;
+ max_drawn_tab = 0;
+ current = 0;
+ previous = 0;
+ } else {
+ offset = MIN(offset, p_count - 1);
+ max_drawn_tab = MIN(max_drawn_tab, p_count - 1);
+ current = MIN(current, p_count - 1);
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ }
+
update();
+ update_minimum_size();
notify_property_list_changed();
}
@@ -478,15 +543,22 @@ int TabBar::get_tab_count() const {
}
void TabBar::set_current_tab(int p_current) {
- if (current == p_current) {
- return;
- }
ERR_FAIL_INDEX(p_current, get_tab_count());
previous = current;
current = p_current;
+ if (current == previous) {
+ emit_signal(SNAME("tab_selected"), current);
+ return;
+ }
+
+ emit_signal(SNAME("tab_selected"), current);
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
emit_signal(SNAME("tab_changed"), p_current);
@@ -515,8 +587,13 @@ bool TabBar::get_offset_buttons_visible() const {
void TabBar::set_tab_title(int p_tab, const String &p_title) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].text = p_title;
+
_shape(p_tab);
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -529,6 +606,7 @@ String TabBar::get_tab_title(int p_tab) const {
void TabBar::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);
@@ -544,24 +622,38 @@ Control::TextDirection TabBar::get_tab_text_direction(int p_tab) const {
void TabBar::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_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
void TabBar::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_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
int TabBar::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;
@@ -571,10 +663,17 @@ int TabBar::get_tab_opentype_feature(int p_tab, const String &p_name) const {
void TabBar::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_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
}
@@ -586,7 +685,12 @@ String TabBar::get_tab_language(int p_tab) const {
void TabBar::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_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -599,7 +703,14 @@ Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
ERR_FAIL_INDEX(p_tab, tabs.size());
tabs.write[p_tab].disabled = p_disabled;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
bool TabBar::is_tab_disabled(int p_tab) const {
@@ -607,15 +718,38 @@ bool TabBar::is_tab_disabled(int p_tab) const {
return tabs[p_tab].disabled;
}
-void TabBar::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button) {
+void TabBar::set_tab_hidden(int p_tab, bool p_hidden) {
ERR_FAIL_INDEX(p_tab, tabs.size());
- tabs.write[p_tab].right_button = p_right_button;
+ tabs.write[p_tab].hidden = p_hidden;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
-Ref<Texture2D> TabBar::get_tab_right_button(int p_tab) const {
+bool TabBar::is_tab_hidden(int p_tab) const {
+ ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
+ return tabs[p_tab].hidden;
+}
+
+void TabBar::set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon) {
+ ERR_FAIL_INDEX(p_tab, tabs.size());
+ tabs.write[p_tab].right_button = p_icon;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+Ref<Texture2D> TabBar::get_tab_button_icon(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), Ref<Texture2D>());
return tabs[p_tab].right_button;
}
@@ -625,35 +759,56 @@ void TabBar::_update_hover() {
return;
}
+ ERR_FAIL_COND(tabs.is_empty());
+
const Point2 &pos = get_local_mouse_position();
- // test hovering to display right or close button.
+ // Test hovering to display right or close button.
int hover_now = -1;
int hover_buttons = -1;
- for (int i = offset; i < tabs.size(); i++) {
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
Rect2 rect = get_tab_rect(i);
if (rect.has_point(pos)) {
hover_now = i;
}
+
if (tabs[i].rb_rect.has_point(pos)) {
rb_hover = i;
cb_hover = -1;
hover_buttons = i;
- break;
} else if (!tabs[i].disabled && tabs[i].cb_rect.has_point(pos)) {
cb_hover = i;
rb_hover = -1;
hover_buttons = i;
+ }
+
+ if (hover_buttons != -1) {
+ update();
break;
}
}
+
if (hover != hover_now) {
hover = hover_now;
- emit_signal(SNAME("tab_hovered"), hover);
+
+ if (hover != -1) {
+ emit_signal(SNAME("tab_hovered"), hover);
+ }
}
if (hover_buttons == -1) { // No hover.
+ int rb_hover_old = rb_hover;
+ int cb_hover_old = cb_hover;
+
rb_hover = hover_buttons;
cb_hover = hover_buttons;
+
+ if (rb_hover != rb_hover_old || cb_hover != cb_hover_old) {
+ update();
+ }
}
}
@@ -672,79 +827,52 @@ void TabBar::_update_cache() {
int limit_minus_buttons = limit - incr->get_width() - decr->get_width();
int w = 0;
- int mw = 0;
- int size_fixed = 0;
- int count_resize = 0;
+
+ max_drawn_tab = tabs.size() - 1;
for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = 0;
- tabs.write[i].size_cache = get_tab_width(i);
- 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;
- } else {
- count_resize++;
- }
- }
+ tabs.write[i].size_text = Math::ceil(tabs[i].text_buf->get_size().x);
+ tabs.write[i].size_cache = get_tab_width(i);
- int m_width = min_width;
- if (count_resize > 0) {
- m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width);
- }
+ if (max_width > 0 && tabs[i].size_cache > max_width) {
+ int size_textless = tabs[i].size_cache - tabs[i].size_text;
+ int mw = MAX(size_textless, max_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_selected;
- } else {
- sb = tab_unselected;
+ tabs.write[i].size_text = MAX(mw - size_textless, 1);
+ tabs.write[i].text_buf->set_width(tabs[i].size_text);
+ tabs.write[i].size_cache = size_textless + tabs[i].size_text;
}
- int lsize = tabs[i].size_cache;
- int slen = tabs[i].size_text;
- if (min_width > 0 && mw > limit_minus_buttons && i != current) {
- if (lsize > m_width) {
- 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(SNAME("hseparation"));
- }
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- slen -= cb->get_width();
- slen -= get_theme_constant(SNAME("hseparation"));
- }
-
- slen = MAX(slen, 1);
- lsize = m_width;
- }
+ if (i < offset || i > max_drawn_tab) {
+ tabs.write[i].ofs_cache = 0;
+ continue;
}
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;
- max_drawn_tab = i;
+ if (tabs[i].hidden) {
+ continue;
+ }
+
+ w += tabs[i].size_cache;
// Check if all tabs would fit inside the area.
- if (i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
- w -= get_tab_width(i);
- max_drawn_tab -= 1;
+ if (clip_tabs && i > offset && (w > limit || (offset > 0 && w > limit_minus_buttons))) {
+ tabs.write[i].ofs_cache = 0;
+
+ w -= tabs[i].size_cache;
+ max_drawn_tab = i - 1;
while (w > limit_minus_buttons && max_drawn_tab > offset) {
- w -= get_tab_width(max_drawn_tab);
- max_drawn_tab -= 1;
- }
+ tabs.write[max_drawn_tab].ofs_cache = 0;
- break;
+ if (!tabs[max_drawn_tab].hidden) {
+ w -= tabs[max_drawn_tab].size_cache;
+ }
+
+ max_drawn_tab--;
+ }
}
}
@@ -752,21 +880,25 @@ void TabBar::_update_cache() {
buttons_visible = offset > 0 || missing_right;
if (tab_alignment == ALIGNMENT_LEFT) {
+ _update_hover();
return;
- } else if (tab_alignment == ALIGNMENT_CENTER) {
+ }
+
+ if (tab_alignment == ALIGNMENT_CENTER) {
w = ((buttons_visible ? limit_minus_buttons : limit) - w) / 2;
} else if (tab_alignment == ALIGNMENT_RIGHT) {
w = (buttons_visible ? limit_minus_buttons : limit) - w;
}
- if (w < 0) {
- w = 0;
- }
-
for (int i = offset; i <= max_drawn_tab; i++) {
tabs.write[i].ofs_cache = w;
- w += tabs.write[i].size_cache;
+
+ if (!tabs[i].hidden) {
+ w += tabs[i].size_cache;
+ }
}
+
+ _update_hover();
}
void TabBar::_on_mouse_exited() {
@@ -780,57 +912,79 @@ void TabBar::_on_mouse_exited() {
void TabBar::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) {
Tab t;
t.text = p_str;
- t.xl_text = atr(p_str);
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(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale());
t.icon = p_icon;
-
tabs.push_back(t);
+
+ _shape(tabs.size() - 1);
_update_cache();
- call_deferred(SNAME("_update_hover"));
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
+
+ if (tabs.size() == 1 && is_inside_tree()) {
+ emit_signal(SNAME("tab_changed"), 0);
+ }
}
void TabBar::clear_tabs() {
+ if (tabs.is_empty()) {
+ return;
+ }
+
tabs.clear();
+ offset = 0;
+ max_drawn_tab = 0;
current = 0;
previous = 0;
- call_deferred(SNAME("_update_hover"));
+
update();
+ update_minimum_size();
notify_property_list_changed();
}
void TabBar::remove_tab(int p_idx) {
ERR_FAIL_INDEX(p_idx, tabs.size());
tabs.remove_at(p_idx);
- if (current >= p_idx) {
+
+ bool is_tab_changing = current == p_idx && !tabs.is_empty();
+
+ if (current >= p_idx && current > 0) {
current--;
}
- _update_cache();
- call_deferred(SNAME("_update_hover"));
- update();
- update_minimum_size();
- if (current < 0) {
- current = 0;
+ if (tabs.is_empty()) {
+ offset = 0;
+ max_drawn_tab = 0;
previous = 0;
- }
- if (current >= tabs.size()) {
- current = tabs.size() - 1;
+ } else {
+ offset = MIN(offset, tabs.size() - 1);
+ max_drawn_tab = MIN(max_drawn_tab, tabs.size() - 1);
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
}
- _ensure_no_over_offset();
+ update();
+ update_minimum_size();
notify_property_list_changed();
+
+ if (is_tab_changing && is_inside_tree()) {
+ emit_signal(SNAME("tab_changed"), current);
+ }
}
Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!drag_to_rearrange_enabled) {
- return Variant();
+ return Control::get_drag_data(p_point); // Allow stuff like TabContainer to override it.
}
int tab_over = get_tab_idx_at_point(p_point);
-
if (tab_over < 0) {
return Variant();
}
@@ -840,27 +994,26 @@ Variant TabBar::get_drag_data(const Point2 &p_point) {
if (!tabs[tab_over].icon.is_null()) {
TextureRect *tf = memnew(TextureRect);
tf->set_texture(tabs[tab_over].icon);
+ tf->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
drag_preview->add_child(tf);
}
+
Label *label = memnew(Label(tabs[tab_over].xl_text));
drag_preview->add_child(label);
- if (!tabs[tab_over].right_button.is_null()) {
- TextureRect *tf = memnew(TextureRect);
- tf->set_texture(tabs[tab_over].right_button);
- drag_preview->add_child(tf);
- }
+
set_drag_preview(drag_preview);
Dictionary drag_data;
drag_data["type"] = "tab_element";
drag_data["tab_element"] = tab_over;
drag_data["from_path"] = get_path();
+
return drag_data;
}
bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
if (!drag_to_rearrange_enabled) {
- return false;
+ return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it.
}
Dictionary d = p_data;
@@ -882,16 +1035,16 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
}
}
}
+
return false;
}
void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
if (!drag_to_rearrange_enabled) {
+ Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it.
return;
}
- int hover_now = get_tab_idx_at_point(p_point);
-
Dictionary d = p_data;
if (!d.has("type")) {
return;
@@ -899,33 +1052,53 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
if (String(d["type"]) == "tab_element") {
int tab_from_id = d["tab_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;
}
+
move_tab(tab_from_id, hover_now);
emit_signal(SNAME("active_tab_rearranged"), hover_now);
set_current_tab(hover_now);
} else if (get_tabs_rearrange_group() != -1) {
// Drag and drop between Tabs.
+
Node *from_node = get_node(from_path);
TabBar *from_tabs = Object::cast_to<TabBar>(from_node);
+
if (from_tabs && from_tabs->get_tabs_rearrange_group() == get_tabs_rearrange_group()) {
if (tab_from_id >= from_tabs->get_tab_count()) {
return;
}
+
Tab moving_tab = from_tabs->tabs[tab_from_id];
if (hover_now < 0) {
hover_now = get_tab_count();
}
- tabs.insert(hover_now, moving_tab);
+
from_tabs->remove_tab(tab_from_id);
+ tabs.insert(hover_now, moving_tab);
+
+ if (tabs.size() > 1) {
+ if (current >= hover_now) {
+ current++;
+ }
+ if (previous >= hover_now) {
+ previous++;
+ }
+ }
+
set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
- _update_cache();
- update();
+ update_minimum_size();
+
+ if (tabs.size() == 1) {
+ emit_signal(SNAME("tab_selected"), 0);
+ emit_signal(SNAME("tab_changed"), 0);
+ }
}
}
}
@@ -946,6 +1119,7 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
tab_alignment = p_alignment;
+
_update_cache();
update();
}
@@ -959,7 +1133,16 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
return;
}
clip_tabs = p_clip_tabs;
+
+ if (!clip_tabs) {
+ offset = 0;
+ max_drawn_tab = 0;
+ }
+
_update_cache();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
update_minimum_size();
}
@@ -968,19 +1151,39 @@ bool TabBar::get_clip_tabs() const {
return clip_tabs;
}
-void TabBar::move_tab(int from, int to) {
- if (from == to) {
+void TabBar::move_tab(int p_from, int p_to) {
+ if (p_from == p_to) {
return;
}
- ERR_FAIL_INDEX(from, tabs.size());
- ERR_FAIL_INDEX(to, tabs.size());
+ ERR_FAIL_INDEX(p_from, tabs.size());
+ ERR_FAIL_INDEX(p_to, tabs.size());
+
+ Tab tab_from = tabs[p_from];
+ tabs.remove_at(p_from);
+ tabs.insert(p_to, tab_from);
- Tab tab_from = tabs[from];
- tabs.remove_at(from);
- tabs.insert(to, tab_from);
+ if (current == p_from) {
+ current = p_to;
+ } else if (current > p_from && current <= p_to) {
+ current--;
+ } else if (current < p_from && current >= p_to) {
+ current++;
+ }
+
+ if (previous == p_from) {
+ previous = p_to;
+ } else if (previous > p_from && previous >= p_to) {
+ previous--;
+ } else if (previous < p_from && previous <= p_to) {
+ previous++;
+ }
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
notify_property_list_changed();
}
@@ -991,37 +1194,49 @@ int TabBar::get_tab_width(int p_idx) const {
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 hseparation = get_theme_constant(SNAME("hseparation"));
- int x = 0;
+ Ref<StyleBox> style;
+
+ if (tabs[p_idx].disabled) {
+ style = tab_disabled;
+ } else if (current == p_idx) {
+ style = tab_selected;
+ } else {
+ style = tab_unselected;
+ }
+ int x = style->get_minimum_size().width;
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
- x += tex->get_width();
- if (!tabs[p_idx].text.is_empty()) {
- x += get_theme_constant(SNAME("hseparation"));
- }
+ x += tex->get_width() + hseparation;
}
- x += Math::ceil(tabs[p_idx].text_buf->get_size().x);
-
- if (tabs[p_idx].disabled) {
- x += tab_disabled->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].text.is_empty()) {
+ x += tabs[p_idx].size_text + hseparation;
}
+ bool close_visible = cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current);
+
if (tabs[p_idx].right_button.is_valid()) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> rb = tabs[p_idx].right_button;
- x += rb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+
+ if (close_visible) {
+ x += btn_style->get_minimum_size().width + rb->get_width();
+ } else {
+ x += btn_style->get_margin(SIDE_LEFT) + rb->get_width() + hseparation;
+ }
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && p_idx == current)) {
+ if (close_visible) {
+ Ref<StyleBox> btn_style = get_theme_stylebox(SNAME("button_highlight"));
Ref<Texture2D> cb = get_theme_icon(SNAME("close"));
- x += cb->get_width();
- x += get_theme_constant(SNAME("hseparation"));
+ x += btn_style->get_margin(SIDE_LEFT) + cb->get_width() + hseparation;
+ }
+
+ if (x > style->get_minimum_size().width) {
+ x -= hseparation;
}
return x;
@@ -1039,8 +1254,12 @@ void TabBar::_ensure_no_over_offset() {
int prev_offset = offset;
int total_w = tabs[max_drawn_tab].ofs_cache + tabs[max_drawn_tab].size_cache - tabs[offset].ofs_cache;
- while (offset > 0) {
- total_w += tabs[offset - 1].size_cache;
+ for (int i = offset; i > 0; i--) {
+ if (tabs[i - 1].hidden) {
+ continue;
+ }
+
+ total_w += tabs[i - 1].size_cache;
if (total_w < limit_minus_buttons) {
offset--;
@@ -1061,7 +1280,7 @@ void TabBar::ensure_tab_visible(int p_idx) {
}
ERR_FAIL_INDEX(p_idx, tabs.size());
- if (p_idx >= offset && p_idx <= max_drawn_tab) {
+ if (tabs[p_idx].hidden || (p_idx >= offset && p_idx <= max_drawn_tab)) {
return;
}
@@ -1079,12 +1298,20 @@ void TabBar::ensure_tab_visible(int p_idx) {
int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
for (int i = max_drawn_tab; i <= p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
total_w += tabs[i].size_cache;
}
int prev_offset = offset;
for (int i = offset; i < p_idx; i++) {
+ if (tabs[i].hidden) {
+ continue;
+ }
+
if (total_w > limit_minus_buttons) {
total_w -= tabs[i].size_cache;
offset++;
@@ -1111,16 +1338,35 @@ Rect2 TabBar::get_tab_rect(int p_tab) const {
void TabBar::set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy) {
ERR_FAIL_INDEX(p_policy, CLOSE_BUTTON_MAX);
cb_displaypolicy = p_policy;
+
_update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
update();
+ update_minimum_size();
}
TabBar::CloseButtonDisplayPolicy TabBar::get_tab_close_display_policy() const {
return cb_displaypolicy;
}
-void TabBar::set_min_width(int p_width) {
- min_width = p_width;
+void TabBar::set_max_tab_width(int p_width) {
+ ERR_FAIL_COND(p_width < 0);
+ max_width = p_width;
+
+ _update_cache();
+ _ensure_no_over_offset();
+ if (scroll_to_selected) {
+ ensure_tab_visible(current);
+ }
+ update();
+ update_minimum_size();
+}
+
+int TabBar::get_max_tab_width() const {
+ return max_width;
}
void TabBar::set_scrolling_enabled(bool p_enabled) {
@@ -1147,6 +1393,17 @@ int TabBar::get_tabs_rearrange_group() const {
return tabs_rearrange_group;
}
+void TabBar::set_scroll_to_selected(bool p_enabled) {
+ scroll_to_selected = p_enabled;
+ if (p_enabled) {
+ ensure_tab_visible(current);
+ }
+}
+
+bool TabBar::get_scroll_to_selected() const {
+ return scroll_to_selected;
+}
+
void TabBar::set_select_with_rmb(bool p_enabled) {
select_with_rmb = p_enabled;
}
@@ -1208,7 +1465,6 @@ void TabBar::_get_property_list(List<PropertyInfo> *p_list) const {
}
void TabBar::_bind_methods() {
- ClassDB::bind_method(D_METHOD("_update_hover"), &TabBar::_update_hover);
ClassDB::bind_method(D_METHOD("set_tab_count", "count"), &TabBar::set_tab_count);
ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count);
ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabBar::set_current_tab);
@@ -1225,10 +1481,15 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_tab_language", "tab_idx"), &TabBar::get_tab_language);
ClassDB::bind_method(D_METHOD("set_tab_icon", "tab_idx", "icon"), &TabBar::set_tab_icon);
ClassDB::bind_method(D_METHOD("get_tab_icon", "tab_idx"), &TabBar::get_tab_icon);
+ ClassDB::bind_method(D_METHOD("set_tab_button_icon", "tab_idx", "icon"), &TabBar::set_tab_button_icon);
+ ClassDB::bind_method(D_METHOD("get_tab_button_icon", "tab_idx"), &TabBar::get_tab_button_icon);
ClassDB::bind_method(D_METHOD("set_tab_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);
ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabBar::is_tab_disabled);
+ ClassDB::bind_method(D_METHOD("set_tab_hidden", "tab_idx", "hidden"), &TabBar::set_tab_hidden);
+ ClassDB::bind_method(D_METHOD("is_tab_hidden", "tab_idx"), &TabBar::is_tab_hidden);
ClassDB::bind_method(D_METHOD("remove_tab", "tab_idx"), &TabBar::remove_tab);
ClassDB::bind_method(D_METHOD("add_tab", "title", "icon"), &TabBar::add_tab, DEFVAL(""), DEFVAL(Ref<Texture2D>()));
+ ClassDB::bind_method(D_METHOD("get_tab_idx_at_point", "point"), &TabBar::get_tab_idx_at_point);
ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabBar::set_tab_alignment);
ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabBar::get_tab_alignment);
ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabBar::set_clip_tabs);
@@ -1240,29 +1501,38 @@ void TabBar::_bind_methods() {
ClassDB::bind_method(D_METHOD("move_tab", "from", "to"), &TabBar::move_tab);
ClassDB::bind_method(D_METHOD("set_tab_close_display_policy", "policy"), &TabBar::set_tab_close_display_policy);
ClassDB::bind_method(D_METHOD("get_tab_close_display_policy"), &TabBar::get_tab_close_display_policy);
+ ClassDB::bind_method(D_METHOD("set_max_tab_width", "width"), &TabBar::set_max_tab_width);
+ ClassDB::bind_method(D_METHOD("get_max_tab_width"), &TabBar::get_max_tab_width);
ClassDB::bind_method(D_METHOD("set_scrolling_enabled", "enabled"), &TabBar::set_scrolling_enabled);
ClassDB::bind_method(D_METHOD("get_scrolling_enabled"), &TabBar::get_scrolling_enabled);
ClassDB::bind_method(D_METHOD("set_drag_to_rearrange_enabled", "enabled"), &TabBar::set_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("get_drag_to_rearrange_enabled"), &TabBar::get_drag_to_rearrange_enabled);
ClassDB::bind_method(D_METHOD("set_tabs_rearrange_group", "group_id"), &TabBar::set_tabs_rearrange_group);
ClassDB::bind_method(D_METHOD("get_tabs_rearrange_group"), &TabBar::get_tabs_rearrange_group);
-
+ ClassDB::bind_method(D_METHOD("set_scroll_to_selected", "enabled"), &TabBar::set_scroll_to_selected);
+ ClassDB::bind_method(D_METHOD("get_scroll_to_selected"), &TabBar::get_scroll_to_selected);
ClassDB::bind_method(D_METHOD("set_select_with_rmb", "enabled"), &TabBar::set_select_with_rmb);
ClassDB::bind_method(D_METHOD("get_select_with_rmb"), &TabBar::get_select_with_rmb);
+ ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_rmb_clicked", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_close_pressed", PropertyInfo(Variant::INT, "tab")));
+ ADD_SIGNAL(MethodInfo("tab_button_pressed", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to")));
- ADD_SIGNAL(MethodInfo("tab_clicked", PropertyInfo(Variant::INT, "tab")));
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::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_tab_width", PROPERTY_HINT_RANGE, "0,99999,1"), "set_max_tab_width", "get_max_tab_width");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrolling_enabled"), "set_scrolling_enabled", "get_scrolling_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_to_rearrange_enabled"), "set_drag_to_rearrange_enabled", "get_drag_to_rearrange_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_rearrange_group"), "set_tabs_rearrange_group", "get_tabs_rearrange_group");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected"), "set_scroll_to_selected", "get_scroll_to_selected");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb");
ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
@@ -1278,5 +1548,6 @@ void TabBar::_bind_methods() {
}
TabBar::TabBar() {
+ set_size(Size2(get_size().width, get_minimum_size().height));
connect("mouse_exited", callable_mp(this, &TabBar::_on_mouse_exited));
}
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index d0055ae4d2..0f2184aca7 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -63,12 +63,11 @@ private:
Ref<TextLine> text_buf;
Ref<Texture2D> icon;
- int ofs_cache = 0;
bool disabled = false;
+ bool hidden = false;
+ int ofs_cache = 0;
int size_cache = 0;
int size_text = 0;
- int x_cache = 0;
- int x_size_cache = 0;
Ref<Texture2D> right_button;
Rect2 rb_rect;
@@ -99,9 +98,10 @@ private:
CloseButtonDisplayPolicy cb_displaypolicy = CLOSE_BUTTON_SHOW_NEVER;
int hover = -1; // Hovered tab.
- int min_width = 0;
+ int max_width = 0;
bool scrolling_enabled = true;
bool drag_to_rearrange_enabled = false;
+ bool scroll_to_selected = true;
int tabs_rearrange_group = -1;
int get_tab_width(int p_idx) const;
@@ -126,7 +126,6 @@ protected:
Variant get_drag_data(const Point2 &p_point) override;
bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
void drop_data(const Point2 &p_point, const Variant &p_data) override;
- int get_tab_idx_at_point(const Point2 &p_point) const;
public:
void add_tab(const String &p_str = "", const Ref<Texture2D> &p_icon = Ref<Texture2D>());
@@ -150,8 +149,13 @@ public:
void set_tab_disabled(int p_tab, bool p_disabled);
bool is_tab_disabled(int p_tab) const;
- void set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_button);
- Ref<Texture2D> get_tab_right_button(int p_tab) const;
+ void set_tab_hidden(int p_tab, bool p_hidden);
+ bool is_tab_hidden(int p_tab) const;
+
+ void set_tab_button_icon(int p_tab, const Ref<Texture2D> &p_icon);
+ Ref<Texture2D> get_tab_button_icon(int p_tab) const;
+
+ int get_tab_idx_at_point(const Point2 &p_point) const;
void set_tab_alignment(AlignmentMode p_alignment);
AlignmentMode get_tab_alignment() const;
@@ -159,7 +163,7 @@ public:
void set_clip_tabs(bool p_clip_tabs);
bool get_clip_tabs() const;
- void move_tab(int from, int to);
+ void move_tab(int p_from, int p_to);
void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy);
CloseButtonDisplayPolicy get_tab_close_display_policy() const;
@@ -187,11 +191,16 @@ public:
void set_tabs_rearrange_group(int p_group_id);
int get_tabs_rearrange_group() const;
+ void set_scroll_to_selected(bool p_enabled);
+ bool get_scroll_to_selected() const;
+
void set_select_with_rmb(bool p_enabled);
bool get_select_with_rmb() const;
void ensure_tab_visible(int p_idx);
- void set_min_width(int p_width);
+
+ void set_max_tab_width(int p_width);
+ int get_max_tab_width() const;
Rect2 get_tab_rect(int p_tab) const;
Size2 get_minimum_size() const override;
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index c3fc08731e..ee61c862b7 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();
- }
- if (popup || buttons_visible_cache) {
- header_width += side_margin;
+ 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();
}
+ } break;
- // 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;
- }
-
- 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,171 @@ 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;
- }
+ panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- if (!all_tabs_in_front) {
- // Draw the tab area.
- panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height));
- }
+ // Draw the popup menu.
+ if (get_popup()) {
+ Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
+ Ref<Texture2D> menu_hl = get_theme_icon(SNAME("menu_highlight"));
- // 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);
- }
- }
+ int x = is_layout_rtl() ? 0 : get_size().width - menu->get_width();
- // 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();
- }
- }
-
- // 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();
-
- 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();
- } 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));
- }
- }
}
} 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_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_constant_override(SNAME("font_size"), get_theme_constant(SNAME("font_size")));
+ tab_bar->add_theme_constant_override(SNAME("icon_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 +338,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 +367,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,10 +375,11 @@ 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;
}
@@ -882,85 +399,195 @@ void TabContainer::drop_data(const Point2 &p_point, const Variant &p_data) {
if (hover_now < 0) {
hover_now = 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()) {
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);
+ add_child(moving_tabc, true);
+
if (hover_now < 0) {
hover_now = get_tab_count() - 1;
}
- move_child(moving_tabc, get_tab_control(hover_now)->get_index());
- set_current_tab(hover_now);
- emit_signal(SNAME("tab_changed"), hover_now);
+
+ move_child(moving_tabc, get_tab_control(hover_now)->get_index(false));
+ if (!is_tab_disabled(hover_now)) {
+ set_current_tab(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::_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());
+
+ _update_margins();
- if (px < tabs_ofs_cache) {
- return -1;
+ 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");
}
+}
- Popup *popup = get_popup();
- if (popup) {
- Ref<Texture2D> menu = get_theme_icon(SNAME("menu"));
- button_ofs += menu->get_width();
+void TabContainer::move_child_notify(Node *p_child) {
+ if (p_child == tab_bar) {
+ return;
+ }
+
+ 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 = c->has_meta("_tab_name") ? String(c->get_meta("_tab_name")) : String(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));
+ }
+}
+
+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 (p_child->has_meta("_tab_name")) {
+ p_child->remove_meta("_tab_name");
}
- 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();
+ 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");
}
- if (px > size.width - button_ofs) {
- return -1;
+}
+
+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);
+}
- // 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) {
+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 +596,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 +623,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 +634,78 @@ 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()) {
+ if (child->has_meta("_tab_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;
- }
-
- //assumed no other tab can be switched to, just hide
+ tab_bar->set_tab_hidden(p_tab, p_hidden);
child->hide();
-}
-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;
+ _update_margins();
+ if (!get_clip_tabs()) {
+ update_minimum_size();
}
}
+bool TabContainer::is_tab_hidden(int p_tab) const {
+ return tab_bar->is_tab_hidden(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 +715,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 +742,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 +780,7 @@ Popup *TabContainer::get_popup() const {
popup_obj_id = ObjectID();
}
}
+
return nullptr;
}
@@ -1162,21 +793,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 +826,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,23 +837,25 @@ 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("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")));
@@ -1219,16 +863,21 @@ void TabContainer::_bind_methods() {
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));
+
connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited));
}
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index 01e71e9fa8..c54934b37b 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -33,65 +33,51 @@
#include "scene/gui/container.h"
#include "scene/gui/popup.h"
-#include "scene/resources/text_line.h"
+#include "scene/gui/tab_bar.h"
class TabContainer : public Container {
GDCLASS(TabContainer, Container);
-public:
- enum AlignmentMode {
- ALIGNMENT_LEFT,
- ALIGNMENT_CENTER,
- ALIGNMENT_RIGHT,
- };
-
-private:
- int first_tab_cache = 0;
- int tabs_ofs_cache = 0;
- int last_tab_cache = 0;
- int current = 0;
- int previous = 0;
+ TabBar *tab_bar;
bool tabs_visible = true;
bool all_tabs_in_front = false;
- bool buttons_visible_cache = false;
bool menu_hovered = false;
- int highlight_arrow = -1;
- AlignmentMode alignment = ALIGNMENT_CENTER;
- int _get_top_margin() const;
mutable ObjectID popup_obj_id;
bool drag_to_rearrange_enabled = false;
bool use_hidden_tabs_for_min_size = false;
- int tabs_rearrange_group = -1;
+ bool theme_changing = false;
- Vector<Ref<TextLine>> text_buf;
- Vector<Control *> _get_tabs() const;
- int _get_tab_width(int p_index) const;
- bool _theme_changing = false;
+ int _get_top_margin() const;
+ Vector<Control *> _get_tab_controls() const;
void _on_theme_changed();
void _repaint();
+ void _refresh_tab_names();
+ void _update_margins();
void _on_mouse_exited();
- void _update_current_tab();
- void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
- void _refresh_texts();
+ void _on_tab_changed(int p_tab);
+ void _on_tab_selected(int p_tab);
+
+ 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);
protected:
- void _child_renamed_callback();
virtual void gui_input(const Ref<InputEvent> &p_event) override;
void _notification(int p_what);
virtual void add_child_notify(Node *p_child) override;
virtual void move_child_notify(Node *p_child) override;
virtual void remove_child_notify(Node *p_child) override;
+ static void _bind_methods();
- Variant get_drag_data(const Point2 &p_point) override;
- bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
- void drop_data(const Point2 &p_point, const Variant &p_data) override;
+public:
int get_tab_idx_at_point(const Point2 &p_point) const;
+ int get_tab_idx_from_control(Control *p_child) const;
- static void _bind_methods();
+ void set_tab_alignment(TabBar::AlignmentMode p_alignment);
+ TabBar::AlignmentMode get_tab_alignment() const;
-public:
- void set_tab_alignment(AlignmentMode p_alignment);
- AlignmentMode get_tab_alignment() const;
+ void set_clip_tabs(bool p_clip_tabs);
+ bool get_clip_tabs() const;
void set_tabs_visible(bool p_visible);
bool are_tabs_visible() const;
@@ -106,10 +92,10 @@ public:
Ref<Texture2D> get_tab_icon(int p_tab) const;
void set_tab_disabled(int p_tab, bool p_disabled);
- bool get_tab_disabled(int p_tab) const;
+ bool is_tab_disabled(int p_tab) const;
void set_tab_hidden(int p_tab, bool p_hidden);
- bool get_tab_hidden(int p_tab) const;
+ bool is_tab_hidden(int p_tab) const;
int get_tab_count() const;
void set_current_tab(int p_current);
@@ -133,9 +119,10 @@ public:
void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs);
bool get_use_hidden_tabs_for_min_size() const;
+ virtual Vector<int> get_allowed_size_flags_horizontal() const override;
+ virtual Vector<int> get_allowed_size_flags_vertical() const override;
+
TabContainer();
};
-VARIANT_ENUM_CAST(TabContainer::AlignmentMode);
-
#endif // TAB_CONTAINER_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index e060d3b901..3c80e3f987 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -43,18 +43,6 @@
#include "scene/main/window.h"
-static bool _is_text_char(char32_t c) {
- return !is_symbol(c);
-}
-
-static bool _is_whitespace(char32_t c) {
- return c == '\t' || c == ' ';
-}
-
-static bool _is_char(char32_t c) {
- return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
-}
-
///////////////////////////////////////////////////////////////////////////////
/// TEXT ///
///////////////////////////////////////////////////////////////////////////////
@@ -187,29 +175,44 @@ void TextEdit::Text::_calculate_max_line_width() {
max_width = width;
}
-void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_ime_text, const Array &p_bidi_override) {
+void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_changed, const String &p_ime_text, const Array &p_bidi_override) {
ERR_FAIL_INDEX(p_line, text.size());
if (font.is_null() || font_size <= 0) {
return; // Not in tree?
}
- text.write[p_line].data_buf->clear();
+ if (p_text_changed) {
+ text.write[p_line].data_buf->clear();
+ }
+
text.write[p_line].data_buf->set_width(width);
text.write[p_line].data_buf->set_direction((TextServer::Direction)direction);
text.write[p_line].data_buf->set_preserve_control(draw_control_chars);
if (p_ime_text.length() > 0) {
- text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(p_ime_text, font, font_size, opentype_features, language);
+ }
if (!p_bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), p_bidi_override);
}
} else {
- text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ if (p_text_changed) {
+ text.write[p_line].data_buf->add_string(text[p_line].data, font, font_size, opentype_features, language);
+ }
if (!text[p_line].bidi_override.is_empty()) {
TS->shaped_text_set_bidi_override(text.write[p_line].data_buf->get_rid(), text[p_line].bidi_override);
}
}
+ if (!p_text_changed) {
+ RID r = text.write[p_line].data_buf->get_rid();
+ int spans = TS->shaped_get_span_count(r);
+ for (int i = 0; i < spans; i++) {
+ TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, opentype_features);
+ }
+ }
+
// Apply tab align.
if (tab_size > 0) {
Vector<float> tabs;
@@ -266,6 +269,24 @@ void TextEdit::Text::invalidate_all_lines() {
}
}
+void TextEdit::Text::invalidate_font() {
+ if (!is_dirty) {
+ return;
+ }
+
+ max_width = -1;
+ line_height = -1;
+
+ if (!font.is_null() && font_size > 0) {
+ font_height = font->get_height(font_size);
+ }
+
+ for (int i = 0; i < text.size(); i++) {
+ invalidate_cache(i, -1, false);
+ }
+ is_dirty = false;
+}
+
void TextEdit::Text::invalidate_all() {
if (!is_dirty) {
return;
@@ -279,7 +300,7 @@ void TextEdit::Text::invalidate_all() {
}
for (int i = 0; i < text.size(); i++) {
- invalidate_cache(i);
+ invalidate_cache(i, -1, true);
}
is_dirty = false;
}
@@ -294,7 +315,7 @@ void TextEdit::Text::clear() {
line.gutters.resize(gutter_count);
line.data = "";
text.insert(0, line);
- invalidate_cache(0);
+ invalidate_cache(0, -1, true);
}
int TextEdit::Text::get_max_width() const {
@@ -306,7 +327,7 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
text.write[p_line].data = p_text;
text.write[p_line].bidi_override = p_bidi_override;
- invalidate_cache(p_line);
+ invalidate_cache(p_line, -1, true);
}
void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
@@ -331,7 +352,7 @@ void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector
line.data = p_text[i];
line.bidi_override = p_bidi_override[i];
text.write[p_at + i] = line;
- invalidate_cache(p_at + i);
+ invalidate_cache(p_at + i, -1, true);
}
}
@@ -411,32 +432,38 @@ void TextEdit::_notification(int p_what) {
}
_update_wrap_at_column(true);
} break;
+
case NOTIFICATION_RESIZED: {
_update_scrollbars();
_update_wrap_at_column();
} break;
+
case NOTIFICATION_VISIBILITY_CHANGED: {
if (is_visible()) {
call_deferred(SNAME("_update_scrollbars"));
call_deferred(SNAME("_update_wrap_at_column"));
}
} break;
+
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
_update_caches();
_update_wrap_at_column(true);
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_IN: {
window_has_focus = true;
draw_caret = true;
update();
} break;
+
case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
window_has_focus = false;
draw_caret = false;
update();
} break;
+
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (scrolling && get_v_scroll() != target_v_scroll) {
double target_y = target_v_scroll - get_v_scroll();
@@ -458,6 +485,7 @@ void TextEdit::_notification(int p_what) {
set_physics_process_internal(false);
}
} break;
+
case NOTIFICATION_DRAW: {
if (first_draw) {
// Size may not be the final one, so attempts to ensure caret was visible may have failed.
@@ -787,8 +815,8 @@ void TextEdit::_notification(int p_what) {
int xpos = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * j)) + tabs;
bool out_of_bounds = (xpos >= xmargin_end + minimap_width);
- bool is_whitespace = _is_whitespace(str[j]);
- if (!is_whitespace) {
+ bool whitespace = is_whitespace(str[j]);
+ if (!whitespace) {
characters++;
if (j < str.length() - 1 && color == previous_color && !out_of_bounds) {
@@ -810,7 +838,7 @@ void TextEdit::_notification(int p_what) {
if (characters > 0) {
previous_color.a *= 0.6;
// take one for zero indexing, and if we hit whitespace / the end of a word.
- int chars = MAX(0, (j - (characters - 1)) - (is_whitespace ? 1 : 0)) + 1;
+ int chars = MAX(0, (j - (characters - 1)) - (whitespace ? 1 : 0)) + 1;
int char_x_ofs = indent_px + ((xmargin_end + minimap_char_size.x) + (minimap_char_size.x * chars)) + tabs;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(size.width - char_x_ofs - minimap_char_size.x * characters, minimap_line_height * i), Point2(minimap_char_size.x * characters, minimap_char_size.y)), previous_color);
@@ -940,7 +968,7 @@ void TextEdit::_notification(int p_what) {
// Give visual indication of empty selected line.
if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) {
- int char_w = font->get_char_size(' ', 0, font_size).width;
+ float char_w = font->get_char_size(' ', 0, font_size).width;
if (rtl) {
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color);
} else {
@@ -1111,7 +1139,7 @@ void TextEdit::_notification(int p_what) {
}
if (!clipped && lookup_symbol_word.length() != 0) { // Highlight word
- if (_is_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '.') {
+ if (is_ascii_char(lookup_symbol_word[0]) || lookup_symbol_word[0] == '_' || lookup_symbol_word[0] == '.') {
int highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, 0);
while (highlighted_word_col != -1) {
Vector<Vector2> sel = TS->shaped_text_get_selection(rid, highlighted_word_col + start, highlighted_word_col + lookup_symbol_word.length() + start);
@@ -1255,7 +1283,8 @@ void TextEdit::_notification(int p_what) {
}
// Carets.
- const int caret_width = get_theme_constant(SNAME("caret_width")) * get_theme_default_base_scale();
+ // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1).
+ const int caret_width = get_theme_constant(SNAME("caret_width")) * MAX(1, get_theme_default_base_scale());
if (!clipped && caret.line == line && line_wrap_index == caret_wrap_index) {
caret.draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index);
@@ -1406,6 +1435,7 @@ void TextEdit::_notification(int p_what) {
}
}
} break;
+
case NOTIFICATION_FOCUS_ENTER: {
if (caret_blink_enabled) {
caret_blink_timer->start();
@@ -1437,6 +1467,7 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, caret_start, caret_end);
}
} break;
+
case NOTIFICATION_FOCUS_EXIT: {
if (caret_blink_enabled) {
caret_blink_timer->stop();
@@ -1446,9 +1477,11 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->window_set_ime_position(Point2(), get_viewport()->get_window_id());
DisplayServer::get_singleton()->window_set_ime_active(false, get_viewport()->get_window_id());
}
- ime_text = "";
- ime_selection = Point2();
- text.invalidate_cache(caret.line, caret.column, ime_text);
+ if (!ime_text.is_empty()) {
+ ime_text = "";
+ ime_selection = Point2();
+ text.invalidate_cache(caret.line, caret.column, true, ime_text);
+ }
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
@@ -1458,6 +1491,7 @@ void TextEdit::_notification(int p_what) {
deselect();
}
} break;
+
case MainLoop::NOTIFICATION_OS_IME_UPDATE: {
if (has_focus()) {
ime_text = DisplayServer::get_singleton()->ime_get_text();
@@ -1470,11 +1504,12 @@ void TextEdit::_notification(int p_what) {
t = ime_text;
}
- text.invalidate_cache(caret.line, caret.column, t, structured_text_parser(st_parser, st_args, t));
+ text.invalidate_cache(caret.line, caret.column, true, t, structured_text_parser(st_parser, st_args, t));
update();
}
} break;
- case Control::NOTIFICATION_DRAG_BEGIN: {
+
+ case NOTIFICATION_DRAG_BEGIN: {
selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
drag_action = true;
dragging_minimap = false;
@@ -1482,7 +1517,8 @@ void TextEdit::_notification(int p_what) {
can_drag_minimap = false;
click_select_held->stop();
} break;
- case Control::NOTIFICATION_DRAG_END: {
+
+ case NOTIFICATION_DRAG_END: {
if (is_drag_successful()) {
if (selection.drag_attempt) {
selection.drag_attempt = false;
@@ -2339,7 +2375,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) {
if (p_all_to_left) {
int caret_current_column = caret.column;
- caret.column = 0;
+ set_caret_column(0);
_remove_text(caret.line, 0, caret.line, caret_current_column);
return;
}
@@ -2449,7 +2485,7 @@ void TextEdit::_update_placeholder() {
return; // Not in tree?
}
- // Placeholder is generally smaller then text docuemnts, and updates less so this should be fast enough for now.
+ // Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now.
placeholder_data_buf->clear();
placeholder_data_buf->set_width(text.get_width());
placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
@@ -2538,7 +2574,7 @@ void TextEdit::_update_caches() {
text.set_draw_control_chars(draw_control_chars);
text.set_font(font);
text.set_font_size(font_size);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
/* Syntax highlighting. */
@@ -2718,7 +2754,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
if (menu_dir) {
@@ -2740,7 +2776,7 @@ void TextEdit::set_opentype_feature(const String &p_name, int p_value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != p_value) {
opentype_features[tag] = p_value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
update();
}
@@ -2757,7 +2793,7 @@ int TextEdit::get_opentype_feature(const String &p_name) const {
void TextEdit::clear_opentype_features() {
opentype_features.clear();
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
update();
}
@@ -2885,15 +2921,20 @@ void TextEdit::_clear() {
end_complex_operation();
return;
}
+ // Cannot merge with above, as we are not part of the tree on creation.
+ int old_text_size = text.size();
+
clear_undo_history();
text.clear();
- caret.column = 0;
- caret.line = 0;
+ set_caret_line(0, false);
+ set_caret_column(0);
caret.x_ofs = 0;
caret.line_ofs = 0;
caret.wrap_ofs = 0;
caret.last_fit_x = 0;
selection.active = false;
+
+ emit_signal(SNAME("lines_edited_from"), old_text_size, 0);
}
void TextEdit::set_text(const String &p_text) {
@@ -2952,14 +2993,16 @@ void TextEdit::set_line(int p_line, const String &p_new_text) {
if (p_line < 0 || p_line >= text.size()) {
return;
}
+ begin_complex_operation();
_remove_text(p_line, 0, p_line, text[p_line].length());
_insert_text(p_line, 0, p_new_text);
- if (caret.line == p_line) {
- caret.column = MIN(caret.column, p_new_text.length());
+ if (caret.line == p_line && caret.column > p_new_text.length()) {
+ set_caret_column(MIN(caret.column, p_new_text.length()), false);
}
if (has_selection() && p_line == selection.to_line && selection.to_column > text[p_line].length()) {
selection.to_column = text[p_line].length();
}
+ end_complex_operation();
}
String TextEdit::get_line(int p_line) const {
@@ -3002,7 +3045,7 @@ int TextEdit::get_first_non_whitespace_column(int p_line) const {
ERR_FAIL_INDEX_V(p_line, text.size(), 0);
int col = 0;
- while (col < text[p_line].length() && _is_whitespace(text[p_line][col])) {
+ while (col < text[p_line].length() && is_whitespace(text[p_line][col])) {
col++;
}
return col;
@@ -3014,8 +3057,10 @@ void TextEdit::swap_lines(int p_from_line, int p_to_line) {
String tmp = get_line(p_from_line);
String tmp2 = get_line(p_to_line);
+ begin_complex_operation();
set_line(p_to_line, tmp);
set_line(p_from_line, tmp2);
+ end_complex_operation();
}
void TextEdit::insert_line_at(int p_at, const String &p_text) {
@@ -3024,7 +3069,7 @@ void TextEdit::insert_line_at(int p_at, const String &p_text) {
_insert_text(p_at, 0, p_text + "\n");
if (caret.line >= p_at) {
// offset caret when located after inserted line
- ++caret.line;
+ set_caret_line(caret.line + 1, false);
}
if (has_selection()) {
if (selection.from_line >= p_at) {
@@ -3587,9 +3632,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro
if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) {
// Validate for whole words.
- if (pos > 0 && _is_text_char(text_line[pos - 1])) {
+ if (pos > 0 && !is_symbol(text_line[pos - 1])) {
is_match = false;
- } else if (pos + p_key.length() < text_line.length() && _is_text_char(text_line[pos + p_key.length()])) {
+ } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) {
is_match = false;
}
}
@@ -3929,6 +3974,7 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
}
}
}
+ bool caret_moved = caret.line != p_line;
caret.line = p_line;
int n_col = _get_char_pos_for_line(caret.last_fit_x, p_line, p_wrap_index);
@@ -3942,15 +3988,16 @@ void TextEdit::set_caret_line(int p_line, bool p_adjust_viewport, bool p_can_be_
n_col -= 1;
}
}
+ caret_moved = (caret_moved || caret.column != n_col);
caret.column = n_col;
- if (p_adjust_viewport) {
+ if (is_inside_tree() && p_adjust_viewport) {
adjust_viewport_to_caret();
}
setting_caret_line = false;
- if (!caret_pos_dirty) {
+ if (caret_moved && !caret_pos_dirty) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed");
}
@@ -3967,6 +4014,7 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) {
p_col = 0;
}
+ bool caret_moved = caret.column != p_col;
caret.column = p_col;
if (caret.column > get_line(caret.line).length()) {
caret.column = get_line(caret.line).length();
@@ -3974,11 +4022,11 @@ void TextEdit::set_caret_column(int p_col, bool p_adjust_viewport) {
caret.last_fit_x = _get_column_x_offset_for_line(caret.column, caret.line);
- if (p_adjust_viewport) {
+ if (is_inside_tree() && p_adjust_viewport) {
adjust_viewport_to_caret();
}
- if (!caret_pos_dirty) {
+ if (caret_moved && !caret_pos_dirty) {
if (is_inside_tree()) {
MessageQueue::get_singleton()->push_call(this, "_emit_caret_changed");
}
@@ -4421,7 +4469,11 @@ int TextEdit::get_visible_line_count() const {
int TextEdit::get_visible_line_count_in_range(int p_from_line, int p_to_line) const {
ERR_FAIL_INDEX_V(p_from_line, text.size(), 0);
ERR_FAIL_INDEX_V(p_to_line, text.size(), 0);
- ERR_FAIL_COND_V(p_from_line > p_to_line, 0);
+
+ // So we can handle inputs in whatever order
+ if (p_from_line > p_to_line) {
+ SWAP(p_from_line, p_to_line);
+ }
/* Returns the total number of (lines + wrapped - hidden). */
if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
@@ -4852,7 +4904,7 @@ void TextEdit::set_draw_control_chars(bool p_enabled) {
menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars);
}
text.set_draw_control_chars(draw_control_chars);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
update();
}
@@ -5319,7 +5371,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
update();
}
@@ -5327,7 +5379,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
text.set_font_features(opentype_features);
- text.invalidate_all();
+ text.invalidate_font();
_update_placeholder();
update();
}
@@ -5616,8 +5668,10 @@ void TextEdit::_generate_context_menu() {
if (editable) {
menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE);
}
- menu->add_separator();
- if (is_selecting_enabled()) {
+ if (selecting_enabled || editable) {
+ menu->add_separator();
+ }
+ if (selecting_enabled) {
menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
}
if (editable) {
@@ -5744,9 +5798,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc
if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) {
p_from_column = col;
- if (col > 0 && _is_text_char(p_search[col - 1])) {
+ if (col > 0 && !is_symbol(p_search[col - 1])) {
col = -1;
- } else if ((col + p_key.length()) < p_search.length() && _is_text_char(p_search[col + p_key.length()])) {
+ } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) {
col = -1;
}
}
@@ -6531,7 +6585,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
emit_signal(SNAME("lines_edited_from"), p_to_line, p_from_line);
}
-TextEdit::TextEdit() {
+TextEdit::TextEdit(const String &p_placeholder) {
placeholder_data_buf.instantiate();
clear();
@@ -6573,5 +6627,7 @@ TextEdit::TextEdit() {
undo_stack_max_size = GLOBAL_GET("gui/common/text_edit_undo_stack_max_size");
+ set_placeholder(p_placeholder);
+
set_editable(true);
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 079890249e..6deaf76e5e 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -210,7 +210,8 @@ private:
int size() const { return text.size(); }
void clear();
- void invalidate_cache(int p_line, int p_column = -1, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_cache(int p_line, int p_column = -1, bool p_text_changed = false, const String &p_ime_text = String(), const Array &p_bidi_override = Array());
+ void invalidate_font();
void invalidate_all();
void invalidate_all_lines();
@@ -939,7 +940,7 @@ public:
void set_draw_spaces(bool p_enabled);
bool is_drawing_spaces() const;
- TextEdit();
+ TextEdit(const String &p_placeholder = String());
};
VARIANT_ENUM_CAST(TextEdit::CaretType);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index da202c1c8f..26acfaaa70 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -37,7 +37,7 @@
Size2 TextureButton::get_minimum_size() const {
Size2 rscale = Control::get_minimum_size();
- if (!expand) {
+ if (!ignore_texture_size) {
if (normal.is_null()) {
if (pressed.is_null()) {
if (hover.is_null()) {
@@ -173,7 +173,8 @@ void TextureButton::_notification(int p_what) {
bool draw_focus = (has_focus() && focused.is_valid());
// If no other texture is valid, try using focused texture.
- if (!texdraw.is_valid() && draw_focus) {
+ bool draw_focus_only = draw_focus && !texdraw.is_valid();
+ if (draw_focus_only) {
texdraw = focused;
}
@@ -181,50 +182,48 @@ void TextureButton::_notification(int p_what) {
size = texdraw->get_size();
_texture_region = Rect2(Point2(), texdraw->get_size());
_tile = false;
- if (expand) {
- switch (stretch_mode) {
- case STRETCH_KEEP:
- size = texdraw->get_size();
- break;
- case STRETCH_SCALE:
- size = get_size();
- break;
- case STRETCH_TILE:
- size = get_size();
- _tile = true;
- break;
- case STRETCH_KEEP_CENTERED:
- ofs = (get_size() - texdraw->get_size()) / 2;
- size = texdraw->get_size();
- break;
- case STRETCH_KEEP_ASPECT_CENTERED:
- case STRETCH_KEEP_ASPECT: {
- Size2 _size = get_size();
- float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
- float tex_height = _size.height;
-
- if (tex_width > _size.width) {
- tex_width = _size.width;
- tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
- }
+ switch (stretch_mode) {
+ case STRETCH_KEEP:
+ size = texdraw->get_size();
+ break;
+ case STRETCH_SCALE:
+ size = get_size();
+ break;
+ case STRETCH_TILE:
+ size = get_size();
+ _tile = true;
+ break;
+ case STRETCH_KEEP_CENTERED:
+ ofs = (get_size() - texdraw->get_size()) / 2;
+ size = texdraw->get_size();
+ break;
+ case STRETCH_KEEP_ASPECT_CENTERED:
+ case STRETCH_KEEP_ASPECT: {
+ Size2 _size = get_size();
+ float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
+ float tex_height = _size.height;
+
+ if (tex_width > _size.width) {
+ tex_width = _size.width;
+ tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
+ }
- if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- ofs.x = (_size.width - tex_width) / 2;
- ofs.y = (_size.height - tex_height) / 2;
- }
- size.width = tex_width;
- size.height = tex_height;
- } break;
- case STRETCH_KEEP_ASPECT_COVERED: {
- size = get_size();
- Size2 tex_size = texdraw->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
- Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
- _texture_region = Rect2(ofs2, size / scale);
- } break;
- }
+ if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
+ ofs.x = (_size.width - tex_width) / 2;
+ ofs.y = (_size.height - tex_height) / 2;
+ }
+ size.width = tex_width;
+ size.height = tex_height;
+ } break;
+ case STRETCH_KEEP_ASPECT_COVERED: {
+ size = get_size();
+ Size2 tex_size = texdraw->get_size();
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+ Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
+ _texture_region = Rect2(ofs2, size / scale);
+ } break;
}
_position_rect = Rect2(ofs, size);
@@ -232,7 +231,7 @@ void TextureButton::_notification(int p_what) {
size.width *= hflip ? -1.0f : 1.0f;
size.height *= vflip ? -1.0f : 1.0f;
- if (texdraw == focused) {
+ if (draw_focus_only) {
// Do nothing, we only needed to calculate the rectangle.
} else if (_tile) {
draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
@@ -257,7 +256,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture);
ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture);
ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
- ClassDB::bind_method(D_METHOD("set_expand", "expand"), &TextureButton::set_expand);
+ ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureButton::set_ignore_texture_size);
ClassDB::bind_method(D_METHOD("set_stretch_mode", "mode"), &TextureButton::set_stretch_mode);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureButton::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureButton::is_flipped_h);
@@ -270,7 +269,7 @@ void TextureButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_disabled_texture"), &TextureButton::get_disabled_texture);
ClassDB::bind_method(D_METHOD("get_focused_texture"), &TextureButton::get_focused_texture);
ClassDB::bind_method(D_METHOD("get_click_mask"), &TextureButton::get_click_mask);
- ClassDB::bind_method(D_METHOD("get_expand"), &TextureButton::get_expand);
+ ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureButton::get_ignore_texture_size);
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode);
ADD_GROUP("Textures", "texture_");
@@ -280,7 +279,7 @@ void TextureButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_disabled_texture", "get_disabled_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_focused_texture", "get_focused_texture");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_ignore_texture_size", "get_ignore_texture_size");
ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_flip_v", "is_flipped_v");
@@ -351,12 +350,12 @@ void TextureButton::set_focused_texture(const Ref<Texture2D> &p_focused) {
focused = p_focused;
};
-bool TextureButton::get_expand() const {
- return expand;
+bool TextureButton::get_ignore_texture_size() const {
+ return ignore_texture_size;
}
-void TextureButton::set_expand(bool p_expand) {
- expand = p_expand;
+void TextureButton::set_ignore_texture_size(bool p_ignore) {
+ ignore_texture_size = p_ignore;
update_minimum_size();
update();
}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 1428a79a1d..5762949acd 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -54,8 +54,8 @@ private:
Ref<Texture2D> disabled;
Ref<Texture2D> focused;
Ref<BitMap> click_mask;
- bool expand = false;
- StretchMode stretch_mode = STRETCH_SCALE;
+ bool ignore_texture_size = false;
+ StretchMode stretch_mode = STRETCH_KEEP;
Rect2 _texture_region;
Rect2 _position_rect;
@@ -85,8 +85,8 @@ public:
Ref<Texture2D> get_focused_texture() const;
Ref<BitMap> get_click_mask() const;
- bool get_expand() const;
- void set_expand(bool p_expand);
+ bool get_ignore_texture_size() const;
+ void set_ignore_texture_size(bool p_ignore);
void set_stretch_mode(StretchMode p_stretch_mode);
StretchMode get_stretch_mode() const;
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index a8cdeb44f5..ecdf55caf0 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -29,84 +29,87 @@
/*************************************************************************/
#include "texture_rect.h"
+
#include "core/core_string_names.h"
#include "servers/rendering_server.h"
void TextureRect::_notification(int p_what) {
- if (p_what == NOTIFICATION_DRAW) {
- if (texture.is_null()) {
- return;
- }
-
- Size2 size;
- Point2 offset;
- Rect2 region;
- bool tile = false;
-
- switch (stretch_mode) {
- case STRETCH_SCALE: {
- size = get_size();
- } break;
- case STRETCH_TILE: {
- size = get_size();
- tile = true;
- } break;
- case STRETCH_KEEP: {
- size = texture->get_size();
- } break;
- case STRETCH_KEEP_CENTERED: {
- offset = (get_size() - texture->get_size()) / 2;
- size = texture->get_size();
- } break;
- case STRETCH_KEEP_ASPECT_CENTERED:
- case STRETCH_KEEP_ASPECT: {
- size = get_size();
- int tex_width = texture->get_width() * size.height / texture->get_height();
- int tex_height = size.height;
-
- if (tex_width > size.width) {
- tex_width = size.width;
- tex_height = texture->get_height() * tex_width / texture->get_width();
- }
-
- if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
- offset.x += (size.width - tex_width) / 2;
- offset.y += (size.height - tex_height) / 2;
- }
-
- size.width = tex_width;
- size.height = tex_height;
- } break;
- case STRETCH_KEEP_ASPECT_COVERED: {
- size = get_size();
-
- Size2 tex_size = texture->get_size();
- Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
- float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
- Size2 scaled_tex_size = tex_size * scale;
-
- region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f;
- region.size = size / scale;
- } break;
- }
-
- Ref<AtlasTexture> p_atlas = texture;
-
- if (p_atlas.is_valid() && region.has_no_area()) {
- Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height());
-
- offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0;
- offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0;
- }
-
- size.width *= hflip ? -1.0f : 1.0f;
- size.height *= vflip ? -1.0f : 1.0f;
-
- if (region.has_no_area()) {
- draw_texture_rect(texture, Rect2(offset, size), tile);
- } else {
- draw_texture_rect_region(texture, Rect2(offset, size), region);
- }
+ switch (p_what) {
+ case NOTIFICATION_DRAW: {
+ if (texture.is_null()) {
+ return;
+ }
+
+ Size2 size;
+ Point2 offset;
+ Rect2 region;
+ bool tile = false;
+
+ switch (stretch_mode) {
+ case STRETCH_SCALE: {
+ size = get_size();
+ } break;
+ case STRETCH_TILE: {
+ size = get_size();
+ tile = true;
+ } break;
+ case STRETCH_KEEP: {
+ size = texture->get_size();
+ } break;
+ case STRETCH_KEEP_CENTERED: {
+ offset = (get_size() - texture->get_size()) / 2;
+ size = texture->get_size();
+ } break;
+ case STRETCH_KEEP_ASPECT_CENTERED:
+ case STRETCH_KEEP_ASPECT: {
+ size = get_size();
+ int tex_width = texture->get_width() * size.height / texture->get_height();
+ int tex_height = size.height;
+
+ if (tex_width > size.width) {
+ tex_width = size.width;
+ tex_height = texture->get_height() * tex_width / texture->get_width();
+ }
+
+ if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
+ offset.x += (size.width - tex_width) / 2;
+ offset.y += (size.height - tex_height) / 2;
+ }
+
+ size.width = tex_width;
+ size.height = tex_height;
+ } break;
+ case STRETCH_KEEP_ASPECT_COVERED: {
+ size = get_size();
+
+ Size2 tex_size = texture->get_size();
+ Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
+ float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
+ Size2 scaled_tex_size = tex_size * scale;
+
+ region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f;
+ region.size = size / scale;
+ } break;
+ }
+
+ Ref<AtlasTexture> p_atlas = texture;
+
+ if (p_atlas.is_valid() && region.has_no_area()) {
+ Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height());
+
+ offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0;
+ offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0;
+ }
+
+ size.width *= hflip ? -1.0f : 1.0f;
+ size.height *= vflip ? -1.0f : 1.0f;
+
+ if (region.has_no_area()) {
+ draw_texture_rect(texture, Rect2(offset, size), tile);
+ } else {
+ draw_texture_rect_region(texture, Rect2(offset, size), region);
+ }
+ } break;
}
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index bc8e81b1ae..ff8d2b88b1 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -37,10 +37,9 @@
#include "core/os/os.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
+#include "scene/gui/box_container.h"
#include "scene/main/window.h"
-#include "box_container.h"
-
#include <limits.h>
Size2 TreeItem::Cell::get_icon_size() const {
@@ -202,7 +201,7 @@ void TreeItem::propagate_check(int p_column, bool p_emit_signal) {
bool ch = cells[p_column].checked;
if (p_emit_signal) {
- tree->emit_signal("check_propagated_to_item", this, p_column);
+ tree->emit_signal(SNAME("check_propagated_to_item"), this, p_column);
}
_propagate_check_through_children(p_column, ch, p_emit_signal);
_propagate_check_through_parents(p_column, p_emit_signal);
@@ -213,7 +212,7 @@ void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, b
while (current) {
current->set_checked(p_column, p_checked);
if (p_emit_signal) {
- current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
}
current->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
current = current->get_next();
@@ -252,7 +251,7 @@ void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal
}
if (p_emit_signal) {
- current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column);
}
current->_propagate_check_through_parents(p_column, p_emit_signal);
}
@@ -905,6 +904,12 @@ String TreeItem::get_button_tooltip(int p_column, int p_idx) const {
return cells[p_column].buttons[p_idx].tooltip;
}
+int TreeItem::get_button_id(int p_column, int p_idx) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), -1);
+ ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1);
+ return cells[p_column].buttons[p_idx].id;
+}
+
void TreeItem::erase_button(int p_column, int p_idx) {
ERR_FAIL_INDEX(p_column, cells.size());
ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size());
@@ -1181,7 +1186,7 @@ void recursive_call_aux(TreeItem *p_item, const StringName &p_method, const Vari
if (!p_item) {
return;
}
- p_item->call(p_method, p_args, p_argcount, r_error);
+ p_item->callp(p_method, p_args, p_argcount, r_error);
TreeItem *c = p_item->get_first_child();
while (c) {
recursive_call_aux(c, p_method, p_args, p_argcount, r_error);
@@ -1283,9 +1288,11 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button);
ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button);
- ClassDB::bind_method(D_METHOD("add_button", "column", "button", "button_idx", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
+ ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL(""));
ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count);
ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip);
+ ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id);
+ ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id);
ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button);
ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button);
ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button);
@@ -1319,10 +1326,10 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children);
ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index);
- ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::_move_before);
- ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::_move_after);
+ ClassDB::bind_method(D_METHOD("move_before", "item"), &TreeItem::move_before);
+ ClassDB::bind_method(D_METHOD("move_after", "item"), &TreeItem::move_after);
- ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::_remove_child);
+ ClassDB::bind_method(D_METHOD("remove_child", "child"), &TreeItem::remove_child);
{
MethodInfo mi;
@@ -1694,8 +1701,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
bool skip = (p_item == root && hide_root);
if (!skip && (p_pos.y + label_h - cache.offset.y) > 0) {
- //draw separation.
- //if (p_item->get_parent()!=root || !hide_root)
+ // Draw separation.
ERR_FAIL_COND_V(cache.font.is_null(), -1);
@@ -2252,11 +2258,6 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c
emit_signal(SNAME("item_selected"));
emitted_row = true;
}
- /*
- if (p_col==i)
- p_current->selected_signal.call(p_col);
- */
-
} else if (c.selected) {
if (p_selected != p_current) {
// Deselect other rows.
@@ -2490,7 +2491,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
/* process selection */
if (p_double_click && (!c.editable || c.mode == TreeItem::CELL_MODE_CUSTOM || c.mode == TreeItem::CELL_MODE_ICON /*|| c.mode==TreeItem::CELL_MODE_CHECK*/)) { //it's confusing for check
-
+ // Emits the "item_activated" signal.
propagate_mouse_activated = true;
incr_search.clear();
@@ -3285,8 +3286,21 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
} else {
Rect2 rect = get_selected()->get_meta("__focus_rect");
Point2 mpos = b->get_position();
+ int icon_size_x = 0;
+ Ref<Texture2D> icon = get_selected()->get_icon(selected_col);
+ if (icon.is_valid()) {
+ Rect2i icon_region = get_selected()->get_icon_region(selected_col);
+ if (icon_region == Rect2i()) {
+ icon_size_x = icon->get_width();
+ } else {
+ icon_size_x = icon_region.size.width;
+ }
+ }
+ // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_double_clicked signal.
if (rtl) {
- mpos.x = get_size().width - mpos.x;
+ mpos.x = get_size().width - (mpos.x + icon_size_x);
+ } else {
+ mpos.x -= icon_size_x;
}
if (rect.has_point(mpos)) {
if (!edit_selected()) {
@@ -3628,178 +3642,187 @@ int Tree::_get_title_button_height() const {
}
void Tree::_notification(int p_what) {
- if (p_what == NOTIFICATION_FOCUS_ENTER) {
- if (get_viewport()) {
- focus_in_id = get_viewport()->get_processed_events_count();
- }
- }
- if (p_what == NOTIFICATION_MOUSE_EXIT) {
- if (cache.hover_type != Cache::CLICK_NONE) {
- cache.hover_type = Cache::CLICK_NONE;
- update();
- }
- }
-
- if (p_what == NOTIFICATION_VISIBILITY_CHANGED) {
- drag_touching = false;
- }
+ switch (p_what) {
+ case NOTIFICATION_FOCUS_ENTER: {
+ if (get_viewport()) {
+ focus_in_id = get_viewport()->get_processed_events_count();
+ }
+ } break;
- if (p_what == NOTIFICATION_ENTER_TREE) {
- update_cache();
- }
- if (p_what == NOTIFICATION_DRAG_END) {
- drop_mode_flags = 0;
- scrolling = false;
- set_physics_process_internal(false);
- update();
- }
- if (p_what == NOTIFICATION_DRAG_BEGIN) {
- single_select_defer = nullptr;
- if (cache.scroll_speed > 0) {
- scrolling = true;
- set_physics_process_internal(true);
- }
- }
- if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
- if (drag_touching) {
- if (drag_touching_deaccel) {
- float pos = v_scroll->get_value();
- pos += drag_speed * get_physics_process_delta_time();
+ case NOTIFICATION_MOUSE_EXIT: {
+ if (cache.hover_type != Cache::CLICK_NONE) {
+ cache.hover_type = Cache::CLICK_NONE;
+ update();
+ }
+ } break;
- bool turnoff = false;
- if (pos < 0) {
- pos = 0;
- turnoff = true;
- set_physics_process_internal(false);
- drag_touching = false;
- drag_touching_deaccel = false;
- }
- if (pos > (v_scroll->get_max() - v_scroll->get_page())) {
- pos = v_scroll->get_max() - v_scroll->get_page();
- turnoff = true;
- }
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+ drag_touching = false;
+ } break;
- v_scroll->set_value(pos);
- float sgn = drag_speed < 0 ? -1 : 1;
- float val = Math::abs(drag_speed);
- val -= 1000 * get_physics_process_delta_time();
+ case NOTIFICATION_ENTER_TREE: {
+ update_cache();
+ } break;
- if (val < 0) {
- turnoff = true;
- }
- drag_speed = sgn * val;
+ case NOTIFICATION_DRAG_END: {
+ drop_mode_flags = 0;
+ scrolling = false;
+ set_physics_process_internal(false);
+ update();
+ } break;
- if (turnoff) {
- set_physics_process_internal(false);
- drag_touching = false;
- drag_touching_deaccel = false;
- }
+ case NOTIFICATION_DRAG_BEGIN: {
+ single_select_defer = nullptr;
+ if (cache.scroll_speed > 0) {
+ scrolling = true;
+ set_physics_process_internal(true);
}
- }
+ } break;
- Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
- if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
- Point2 point;
+ case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
+ if (drag_touching) {
+ if (drag_touching_deaccel) {
+ float pos = v_scroll->get_value();
+ pos += drag_speed * get_physics_process_delta_time();
- if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
- point.x = mouse_position.x - cache.scroll_border;
- } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
- point.x = mouse_position.x - (get_size().width - cache.scroll_border);
- }
+ bool turnoff = false;
+ if (pos < 0) {
+ pos = 0;
+ turnoff = true;
+ set_physics_process_internal(false);
+ drag_touching = false;
+ drag_touching_deaccel = false;
+ }
+ if (pos > (v_scroll->get_max() - v_scroll->get_page())) {
+ pos = v_scroll->get_max() - v_scroll->get_page();
+ turnoff = true;
+ }
+
+ v_scroll->set_value(pos);
+ float sgn = drag_speed < 0 ? -1 : 1;
+ float val = Math::abs(drag_speed);
+ val -= 1000 * get_physics_process_delta_time();
+
+ if (val < 0) {
+ turnoff = true;
+ }
+ drag_speed = sgn * val;
- if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
- point.y = mouse_position.y - cache.scroll_border;
- } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
- point.y = mouse_position.y - (get_size().height - cache.scroll_border);
+ if (turnoff) {
+ set_physics_process_internal(false);
+ drag_touching = false;
+ drag_touching_deaccel = false;
+ }
+ }
}
- point *= cache.scroll_speed * get_physics_process_delta_time();
- point += get_scroll();
- h_scroll->set_value(point.x);
- v_scroll->set_value(point.y);
- }
- }
+ Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position();
+ if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) {
+ Point2 point;
- if (p_what == NOTIFICATION_DRAW) {
- update_cache();
- update_scrollbars();
- RID ci = get_canvas_item();
+ if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) {
+ point.x = mouse_position.x - cache.scroll_border;
+ } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) {
+ point.x = mouse_position.x - (get_size().width - cache.scroll_border);
+ }
- Ref<StyleBox> bg = cache.bg;
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
+ if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) {
+ point.y = mouse_position.y - cache.scroll_border;
+ } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) {
+ point.y = mouse_position.y - (get_size().height - cache.scroll_border);
+ }
- Point2 draw_ofs;
- draw_ofs += bg->get_offset();
- Size2 draw_size = get_size() - bg->get_minimum_size();
- if (h_scroll->is_visible()) {
- draw_size.width -= h_scroll->get_minimum_size().width;
- }
+ point *= cache.scroll_speed * get_physics_process_delta_time();
+ point += get_scroll();
+ h_scroll->set_value(point.x);
+ v_scroll->set_value(point.y);
+ }
+ } break;
- bg->draw(ci, Rect2(Point2(), get_size()));
+ case NOTIFICATION_DRAW: {
+ update_cache();
+ update_scrollbars();
+ RID ci = get_canvas_item();
+
+ Ref<StyleBox> bg = cache.bg;
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+
+ Point2 draw_ofs;
+ draw_ofs += bg->get_offset();
+ Size2 draw_size = get_size() - bg->get_minimum_size();
+ if (h_scroll->is_visible()) {
+ draw_size.width -= h_scroll->get_minimum_size().width;
+ }
- int tbh = _get_title_button_height();
+ bg->draw(ci, Rect2(Point2(), get_size()));
- draw_ofs.y += tbh;
- draw_size.y -= tbh;
+ int tbh = _get_title_button_height();
- cache.rtl = is_layout_rtl();
+ draw_ofs.y += tbh;
+ draw_size.y -= tbh;
- if (root && get_size().x > 0 && get_size().y > 0) {
- draw_item(Point2(), draw_ofs, draw_size, root);
- }
+ cache.rtl = is_layout_rtl();
- if (show_column_titles) {
- //title buttons
- int ofs2 = cache.bg->get_margin(SIDE_LEFT);
- for (int i = 0; i < columns.size(); i++) {
- Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button);
- Ref<Font> f = cache.tb_font;
- Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
- if (cache.rtl) {
- tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
- }
- sb->draw(ci, tbrect);
- ofs2 += tbrect.size.width;
- //text
- int clip_w = tbrect.size.width - sb->get_minimum_size().width;
- columns.write[i].text_buf->set_width(clip_w);
-
- Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
+ if (root && get_size().x > 0 && get_size().y > 0) {
+ draw_item(Point2(), draw_ofs, draw_size, root);
}
- }
- // Draw the background focus outline last, so that it is drawn in front of the section headings.
- // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
- if (has_focus()) {
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
- const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus"));
- bg_focus->draw(ci, Rect2(Point2(), get_size()));
- RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
- }
- }
+ if (show_column_titles) {
+ //title buttons
+ int ofs2 = cache.bg->get_margin(SIDE_LEFT);
+ for (int i = 0; i < columns.size(); i++) {
+ Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button);
+ Ref<Font> f = cache.tb_font;
+ Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh);
+ if (cache.rtl) {
+ tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x;
+ }
+ sb->draw(ci, tbrect);
+ ofs2 += tbrect.size.width;
+ //text
+ int clip_w = tbrect.size.width - sb->get_minimum_size().width;
+ columns.write[i].text_buf->set_width(clip_w);
+
+ Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2);
+ if (outline_size > 0 && font_outline_color.a > 0) {
+ columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
+ }
+ columns[i].text_buf->draw(ci, text_pos, cache.title_button_color);
+ }
+ }
- if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
- update_cache();
- _update_all();
- }
+ // Draw the background focus outline last, so that it is drawn in front of the section headings.
+ // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling.
+ if (has_focus()) {
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true);
+ const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus"));
+ bg_focus->draw(ci, Rect2(Point2(), get_size()));
+ RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false);
+ }
+ } break;
- if (p_what == NOTIFICATION_RESIZED || p_what == NOTIFICATION_TRANSFORM_CHANGED) {
- if (popup_edited_item != nullptr) {
- Rect2 rect = popup_edited_item->get_meta("__focus_rect");
- Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
- Point2i textedpos = get_global_position() + rect.position - ofs;
+ case NOTIFICATION_THEME_CHANGED:
+ case NOTIFICATION_LAYOUT_DIRECTION_CHANGED:
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ update_cache();
+ _update_all();
+ } break;
- if (cache.text_editor_position != textedpos) {
- cache.text_editor_position = textedpos;
- text_editor->set_position(textedpos);
- value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
+ case NOTIFICATION_RESIZED:
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ if (popup_edited_item != nullptr) {
+ Rect2 rect = popup_edited_item->get_meta("__focus_rect");
+ Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2);
+ Point2i textedpos = get_global_position() + rect.position - ofs;
+
+ if (cache.text_editor_position != textedpos) {
+ cache.text_editor_position = textedpos;
+ text_editor->set_position(textedpos);
+ value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height));
+ }
}
- }
+ } break;
}
}
@@ -4049,10 +4072,6 @@ int Tree::get_edited_column() const {
}
TreeItem *Tree::get_next_selected(TreeItem *p_item) {
- /*
- if (!p_item)
- return nullptr;
- */
if (!root) {
return nullptr;
}
@@ -4409,21 +4428,29 @@ Point2 Tree::get_scroll() const {
return ofs;
}
-void Tree::scroll_to_item(TreeItem *p_item) {
+void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) {
if (!is_visible_in_tree()) {
- // hack to work around crash in get_item_rect() if Tree is not in tree.
- return;
+ return; // Hack to work around crash in get_item_rect() if Tree is not in tree.
}
- // make sure the scrollbar min and max are up to date with latest changes.
update_scrollbars();
- const Rect2 r = get_item_rect(p_item);
+ const real_t tree_height = get_size().y;
+ const Rect2 item_rect = get_item_rect(p_item);
+ const real_t item_y = item_rect.position.y;
+ const real_t item_height = item_rect.size.y + cache.vseparation;
- if (r.position.y <= v_scroll->get_value()) {
- v_scroll->set_value(r.position.y);
- } else if (r.position.y + r.size.y + 2 * cache.vseparation > v_scroll->get_value() + get_size().y) {
- v_scroll->set_value(r.position.y + r.size.y + 2 * cache.vseparation - get_size().y);
+ if (p_center_on_item) {
+ v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f);
+ } else {
+ if (item_y < v_scroll->get_value()) {
+ v_scroll->set_value(item_y);
+ } else {
+ const real_t new_position = item_y + item_height - tree_height;
+ if (new_position > v_scroll->get_value()) {
+ v_scroll->set_value(new_position);
+ }
+ }
}
}
@@ -4848,6 +4875,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position);
ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position);
ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position);
+ ClassDB::bind_method(D_METHOD("get_button_id_at_position", "position"), &Tree::get_button_id_at_position);
ClassDB::bind_method(D_METHOD("ensure_cursor_is_visible"), &Tree::ensure_cursor_is_visible);
@@ -4868,7 +4896,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language);
ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll);
- ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::scroll_to_item);
+ ClassDB::bind_method(D_METHOD("scroll_to_item", "item", "center_on_item"), &Tree::scroll_to_item, DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled);
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index fd65f90c49..dc786de6dc 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -188,16 +188,6 @@ protected:
return d;
}
- void _remove_child(Object *p_child) {
- remove_child(Object::cast_to<TreeItem>(p_child));
- }
-
- void _move_before(Object *p_item) {
- move_before(Object::cast_to<TreeItem>(p_item));
- }
- void _move_after(Object *p_item) {
- move_after(Object::cast_to<TreeItem>(p_item));
- }
Variant _call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
@@ -258,6 +248,7 @@ public:
int get_button_count(int p_column) const;
String get_button_tooltip(int p_column, int p_idx) const;
Ref<Texture2D> get_button(int p_column, int p_idx) const;
+ int get_button_id(int p_column, int p_idx) const;
void erase_button(int p_column, int p_idx);
int get_button_by_id(int p_column, int p_id) const;
void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button);
@@ -692,7 +683,7 @@ public:
TreeItem *get_item_with_text(const String &p_find) const;
Point2 get_scroll() const;
- void scroll_to_item(TreeItem *p_item);
+ void scroll_to_item(TreeItem *p_item, bool p_center_on_item = false);
void set_h_scroll_enabled(bool p_enable);
bool is_h_scroll_enabled() const;
void set_v_scroll_enabled(bool p_enable);
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 1f2a8c8aa1..d7c76aa070 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -134,7 +134,6 @@ void VideoStreamPlayer::_notification(int p_notification) {
if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) {
play();
}
-
} break;
case NOTIFICATION_EXIT_TREE: {
@@ -162,7 +161,6 @@ void VideoStreamPlayer::_notification(int p_notification) {
if (!playback->is_playing()) {
emit_signal(SceneStringNames::get_singleton()->finished);
}
-
} break;
case NOTIFICATION_DRAW: {
@@ -175,10 +173,9 @@ void VideoStreamPlayer::_notification(int p_notification) {
Size2 s = expand ? get_size() : texture->get_size();
draw_texture_rect(texture, Rect2(Point2(), s), false);
-
} break;
- };
-};
+ }
+}
Size2 VideoStreamPlayer::get_minimum_size() const {
if (!expand && !texture.is_null()) {
@@ -243,11 +240,11 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
if (!expand) {
update_minimum_size();
}
-};
+}
Ref<VideoStream> VideoStreamPlayer::get_stream() const {
return stream;
-};
+}
void VideoStreamPlayer::play() {
ERR_FAIL_COND(!is_inside_tree());
@@ -257,10 +254,8 @@ void VideoStreamPlayer::play() {
playback->stop();
playback->play();
set_process_internal(true);
- // AudioServer::get_singleton()->stream_set_active(stream_rid,true);
- // AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
last_audio_time = 0;
-};
+}
void VideoStreamPlayer::stop() {
if (!is_inside_tree()) {
@@ -271,11 +266,10 @@ void VideoStreamPlayer::stop() {
}
playback->stop();
- // AudioServer::get_singleton()->stream_set_active(stream_rid,false);
resampler.flush();
set_process_internal(false);
last_audio_time = 0;
-};
+}
bool VideoStreamPlayer::is_playing() const {
if (playback.is_null()) {
@@ -283,16 +277,16 @@ bool VideoStreamPlayer::is_playing() const {
}
return playback->is_playing();
-};
+}
void VideoStreamPlayer::set_paused(bool p_paused) {
paused = p_paused;
if (playback.is_valid()) {
playback->set_paused(p_paused);
set_process_internal(!p_paused);
- };
+ }
last_audio_time = 0;
-};
+}
bool VideoStreamPlayer::is_paused() const {
return paused;
@@ -316,11 +310,11 @@ int VideoStreamPlayer::get_audio_track() const {
void VideoStreamPlayer::set_volume(float p_vol) {
volume = p_vol;
-};
+}
float VideoStreamPlayer::get_volume() const {
return volume;
-};
+}
void VideoStreamPlayer::set_volume_db(float p_db) {
if (p_db < -79) {
@@ -328,7 +322,7 @@ void VideoStreamPlayer::set_volume_db(float p_db) {
} else {
set_volume(Math::db2linear(p_db));
}
-};
+}
float VideoStreamPlayer::get_volume_db() const {
if (volume == 0) {
@@ -336,21 +330,21 @@ float VideoStreamPlayer::get_volume_db() const {
} else {
return Math::linear2db(volume);
}
-};
+}
String VideoStreamPlayer::get_stream_name() const {
if (stream.is_null()) {
return "<No Stream>";
}
return stream->get_name();
-};
+}
float VideoStreamPlayer::get_stream_position() const {
if (playback.is_null()) {
return 0;
}
return playback->get_playback_position();
-};
+}
void VideoStreamPlayer::set_stream_position(float p_position) {
if (playback.is_valid()) {
@@ -368,14 +362,14 @@ Ref<Texture2D> VideoStreamPlayer::get_video_texture() const {
void VideoStreamPlayer::set_autoplay(bool p_enable) {
autoplay = p_enable;
-};
+}
bool VideoStreamPlayer::has_autoplay() const {
return autoplay;
-};
+}
void VideoStreamPlayer::set_bus(const StringName &p_bus) {
- //if audio is active, must lock this
+ // If audio is active, must lock this.
AudioServer::get_singleton()->lock();
bus = p_bus;
AudioServer::get_singleton()->unlock();
@@ -449,7 +443,6 @@ void VideoStreamPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream");
- //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ;
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01"), "set_volume_db", "get_volume_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_RANGE, "0,15,0.01,exp", PROPERTY_USAGE_NONE), "set_volume", "get_volume");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay");
@@ -464,7 +457,5 @@ void VideoStreamPlayer::_bind_methods() {
VideoStreamPlayer::VideoStreamPlayer() {}
VideoStreamPlayer::~VideoStreamPlayer() {
- // if (stream_rid.is_valid())
- // AudioServer::get_singleton()->free(stream_rid);
- resampler.clear(); //Not necessary here, but make in consistent with other "stream_player" classes
-};
+ resampler.clear(); // Not necessary here, but make in consistent with other "stream_player" classes.
+}