summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/aspect_ratio_container.cpp26
-rw-r--r--scene/gui/aspect_ratio_container.h26
-rw-r--r--scene/gui/base_button.cpp8
-rw-r--r--scene/gui/base_button.h4
-rw-r--r--scene/gui/box_container.cpp40
-rw-r--r--scene/gui/box_container.h20
-rw-r--r--scene/gui/button.cpp120
-rw-r--r--scene/gui/button.h25
-rw-r--r--scene/gui/center_container.cpp6
-rw-r--r--scene/gui/center_container.h4
-rw-r--r--scene/gui/check_box.cpp6
-rw-r--r--scene/gui/check_box.h10
-rw-r--r--scene/gui/check_button.cpp6
-rw-r--r--scene/gui/check_button.h10
-rw-r--r--scene/gui/code_edit.cpp139
-rw-r--r--scene/gui/code_edit.h4
-rw-r--r--scene/gui/color_picker.cpp6
-rw-r--r--scene/gui/color_picker.h4
-rw-r--r--scene/gui/color_rect.cpp4
-rw-r--r--scene/gui/color_rect.h4
-rw-r--r--scene/gui/container.cpp12
-rw-r--r--scene/gui/container.h4
-rw-r--r--scene/gui/control.cpp80
-rw-r--r--scene/gui/control.h8
-rw-r--r--scene/gui/dialogs.cpp8
-rw-r--r--scene/gui/dialogs.h4
-rw-r--r--scene/gui/file_dialog.cpp48
-rw-r--r--scene/gui/file_dialog.h6
-rw-r--r--scene/gui/flow_container.cpp252
-rw-r--r--scene/gui/flow_container.h76
-rw-r--r--scene/gui/gradient_edit.cpp8
-rw-r--r--scene/gui/gradient_edit.h6
-rw-r--r--scene/gui/graph_edit.cpp163
-rw-r--r--scene/gui/graph_edit.h29
-rw-r--r--scene/gui/graph_node.cpp18
-rw-r--r--scene/gui/graph_node.h4
-rw-r--r--scene/gui/grid_container.cpp8
-rw-r--r--scene/gui/grid_container.h4
-rw-r--r--scene/gui/item_list.cpp42
-rw-r--r--scene/gui/item_list.h8
-rw-r--r--scene/gui/label.cpp221
-rw-r--r--scene/gui/label.h45
-rw-r--r--scene/gui/line_edit.cpp125
-rw-r--r--scene/gui/line_edit.h20
-rw-r--r--scene/gui/link_button.cpp20
-rw-r--r--scene/gui/link_button.h4
-rw-r--r--scene/gui/margin_container.cpp6
-rw-r--r--scene/gui/margin_container.h4
-rw-r--r--scene/gui/menu_button.cpp8
-rw-r--r--scene/gui/menu_button.h4
-rw-r--r--scene/gui/nine_patch_rect.cpp8
-rw-r--r--scene/gui/nine_patch_rect.h4
-rw-r--r--scene/gui/option_button.cpp111
-rw-r--r--scene/gui/option_button.h11
-rw-r--r--scene/gui/panel.cpp4
-rw-r--r--scene/gui/panel.h4
-rw-r--r--scene/gui/panel_container.cpp4
-rw-r--r--scene/gui/panel_container.h4
-rw-r--r--scene/gui/popup.cpp4
-rw-r--r--scene/gui/popup.h4
-rw-r--r--scene/gui/popup_menu.cpp147
-rw-r--r--scene/gui/popup_menu.h5
-rw-r--r--scene/gui/progress_bar.cpp4
-rw-r--r--scene/gui/progress_bar.h4
-rw-r--r--scene/gui/range.cpp4
-rw-r--r--scene/gui/range.h4
-rw-r--r--scene/gui/reference_rect.cpp4
-rw-r--r--scene/gui/reference_rect.h4
-rw-r--r--scene/gui/rich_text_effect.cpp4
-rw-r--r--scene/gui/rich_text_effect.h4
-rw-r--r--scene/gui/rich_text_label.cpp300
-rw-r--r--scene/gui/rich_text_label.h46
-rw-r--r--scene/gui/scroll_bar.cpp4
-rw-r--r--scene/gui/scroll_bar.h4
-rw-r--r--scene/gui/scroll_container.cpp113
-rw-r--r--scene/gui/scroll_container.h43
-rw-r--r--scene/gui/separator.cpp4
-rw-r--r--scene/gui/separator.h4
-rw-r--r--scene/gui/slider.cpp14
-rw-r--r--scene/gui/slider.h4
-rw-r--r--scene/gui/spin_box.cpp28
-rw-r--r--scene/gui/spin_box.h8
-rw-r--r--scene/gui/split_container.cpp6
-rw-r--r--scene/gui/split_container.h4
-rw-r--r--scene/gui/subviewport_container.cpp5
-rw-r--r--scene/gui/subviewport_container.h4
-rw-r--r--scene/gui/tab_bar.cpp526
-rw-r--r--scene/gui/tab_bar.h34
-rw-r--r--scene/gui/tab_container.cpp69
-rw-r--r--scene/gui/tab_container.h20
-rw-r--r--scene/gui/text_edit.cpp379
-rw-r--r--scene/gui/text_edit.h32
-rw-r--r--scene/gui/texture_button.cpp26
-rw-r--r--scene/gui/texture_button.h4
-rw-r--r--scene/gui/texture_progress_bar.cpp15
-rw-r--r--scene/gui/texture_progress_bar.h4
-rw-r--r--scene/gui/texture_rect.cpp32
-rw-r--r--scene/gui/texture_rect.h13
-rw-r--r--scene/gui/tree.cpp141
-rw-r--r--scene/gui/tree.h25
-rw-r--r--scene/gui/video_stream_player.cpp (renamed from scene/gui/video_player.cpp)138
-rw-r--r--scene/gui/video_stream_player.h (renamed from scene/gui/video_player.h)20
-rw-r--r--scene/gui/view_panner.cpp142
-rw-r--r--scene/gui/view_panner.h68
104 files changed, 2755 insertions, 1583 deletions
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index fb6fa9dec9..181d1bf33b 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -60,12 +60,12 @@ void AspectRatioContainer::set_stretch_mode(StretchMode p_mode) {
queue_sort();
}
-void AspectRatioContainer::set_alignment_horizontal(AlignMode p_alignment_horizontal) {
+void AspectRatioContainer::set_alignment_horizontal(AlignmentMode p_alignment_horizontal) {
alignment_horizontal = p_alignment_horizontal;
queue_sort();
}
-void AspectRatioContainer::set_alignment_vertical(AlignMode p_alignment_vertical) {
+void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vertical) {
alignment_vertical = p_alignment_vertical;
queue_sort();
}
@@ -107,25 +107,25 @@ void AspectRatioContainer::_notification(int p_what) {
float align_x = 0.5;
switch (alignment_horizontal) {
- case ALIGN_BEGIN: {
+ case ALIGNMENT_BEGIN: {
align_x = 0.0;
} break;
- case ALIGN_CENTER: {
+ case ALIGNMENT_CENTER: {
align_x = 0.5;
} break;
- case ALIGN_END: {
+ case ALIGNMENT_END: {
align_x = 1.0;
} break;
}
float align_y = 0.5;
switch (alignment_vertical) {
- case ALIGN_BEGIN: {
+ case ALIGNMENT_BEGIN: {
align_y = 0.0;
} break;
- case ALIGN_CENTER: {
+ case ALIGNMENT_CENTER: {
align_y = 0.5;
} break;
- case ALIGN_END: {
+ case ALIGNMENT_END: {
align_y = 1.0;
} break;
}
@@ -166,7 +166,7 @@ void AspectRatioContainer::_bind_methods() {
BIND_ENUM_CONSTANT(STRETCH_FIT);
BIND_ENUM_CONSTANT(STRETCH_COVER);
- BIND_ENUM_CONSTANT(ALIGN_BEGIN);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_END);
+ BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_END);
}
diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h
index c95c6a7274..4a168bad14 100644
--- a/scene/gui/aspect_ratio_container.h
+++ b/scene/gui/aspect_ratio_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -48,17 +48,17 @@ public:
STRETCH_FIT,
STRETCH_COVER,
};
- enum AlignMode {
- ALIGN_BEGIN,
- ALIGN_CENTER,
- ALIGN_END,
+ enum AlignmentMode {
+ ALIGNMENT_BEGIN,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_END,
};
private:
float ratio = 1.0;
StretchMode stretch_mode = STRETCH_FIT;
- AlignMode alignment_horizontal = ALIGN_CENTER;
- AlignMode alignment_vertical = ALIGN_CENTER;
+ AlignmentMode alignment_horizontal = ALIGNMENT_CENTER;
+ AlignmentMode alignment_vertical = ALIGNMENT_CENTER;
public:
void set_ratio(float p_ratio);
@@ -67,14 +67,14 @@ public:
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const { return stretch_mode; }
- void set_alignment_horizontal(AlignMode p_alignment_horizontal);
- AlignMode get_alignment_horizontal() const { return alignment_horizontal; }
+ void set_alignment_horizontal(AlignmentMode p_alignment_horizontal);
+ AlignmentMode get_alignment_horizontal() const { return alignment_horizontal; }
- void set_alignment_vertical(AlignMode p_alignment_vertical);
- AlignMode get_alignment_vertical() const { return alignment_vertical; }
+ void set_alignment_vertical(AlignmentMode p_alignment_vertical);
+ AlignmentMode get_alignment_vertical() const { return alignment_vertical; }
};
VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode);
-VARIANT_ENUM_CAST(AspectRatioContainer::AlignMode);
+VARIANT_ENUM_CAST(AspectRatioContainer::AlignmentMode);
#endif // ASPECT_RATIO_CONTAINER_H
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index 9f712ed478..eee7663b09 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -355,8 +355,8 @@ String BaseButton::get_tooltip(const Point2 &p_pos) const {
String tooltip = Control::get_tooltip(p_pos);
if (shortcut_in_tooltip && shortcut.is_valid() && shortcut->has_valid_event()) {
String text = shortcut->get_name() + " (" + shortcut->get_as_text() + ")";
- if (tooltip != String() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
- text += "\n" + tooltip;
+ if (!tooltip.is_empty() && shortcut->get_name().nocasecmp_to(tooltip) != 0) {
+ text += "\n" + atr(tooltip);
}
tooltip = text;
}
diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h
index 3ea59c3ff9..0bcad4fc0e 100644
--- a/scene/gui/base_button.h
+++ b/scene/gui/base_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp
index cb9f13e970..9827bd0cef 100644
--- a/scene/gui/box_container.cpp
+++ b/scene/gui/box_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -154,29 +154,29 @@ void BoxContainer::_resort() {
int ofs = 0;
if (!has_stretched) {
if (!vertical) {
- switch (align) {
- case ALIGN_BEGIN:
+ switch (alignment) {
+ case ALIGNMENT_BEGIN:
if (rtl) {
ofs = stretch_diff;
}
break;
- case ALIGN_CENTER:
+ case ALIGNMENT_CENTER:
ofs = stretch_diff / 2;
break;
- case ALIGN_END:
+ case ALIGNMENT_END:
if (!rtl) {
ofs = stretch_diff;
}
break;
}
} else {
- switch (align) {
- case ALIGN_BEGIN:
+ switch (alignment) {
+ case ALIGNMENT_BEGIN:
break;
- case ALIGN_CENTER:
+ case ALIGNMENT_CENTER:
ofs = stretch_diff / 2;
break;
- case ALIGN_END:
+ case ALIGNMENT_END:
ofs = stretch_diff;
break;
}
@@ -295,7 +295,7 @@ void BoxContainer::_notification(int p_what) {
_resort();
} break;
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
@@ -304,13 +304,13 @@ void BoxContainer::_notification(int p_what) {
}
}
-void BoxContainer::set_alignment(AlignMode p_align) {
- align = p_align;
+void BoxContainer::set_alignment(AlignmentMode p_alignment) {
+ alignment = p_alignment;
_resort();
}
-BoxContainer::AlignMode BoxContainer::get_alignment() const {
- return align;
+BoxContainer::AlignmentMode BoxContainer::get_alignment() const {
+ return alignment;
}
Control *BoxContainer::add_spacer(bool p_begin) {
@@ -340,9 +340,9 @@ void BoxContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_alignment"), &BoxContainer::get_alignment);
ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &BoxContainer::set_alignment);
- BIND_ENUM_CONSTANT(ALIGN_BEGIN);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_END);
+ BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_END);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment");
}
@@ -351,11 +351,11 @@ MarginContainer *VBoxContainer::add_margin_child(const String &p_label, Control
Label *l = memnew(Label);
l->set_theme_type_variation("HeaderSmall");
l->set_text(p_label);
- add_child(l, false, INTERNAL_MODE_FRONT);
+ add_child(l);
MarginContainer *mc = memnew(MarginContainer);
mc->add_theme_constant_override("margin_left", 0);
mc->add_child(p_control, true);
- add_child(mc, false, INTERNAL_MODE_FRONT);
+ add_child(mc);
if (p_expand) {
mc->set_v_size_flags(SIZE_EXPAND_FILL);
}
diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h
index 23feea565c..68d55e1aaf 100644
--- a/scene/gui/box_container.h
+++ b/scene/gui/box_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,15 +37,15 @@ class BoxContainer : public Container {
GDCLASS(BoxContainer, Container);
public:
- enum AlignMode {
- ALIGN_BEGIN,
- ALIGN_CENTER,
- ALIGN_END
+ enum AlignmentMode {
+ ALIGNMENT_BEGIN,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_END
};
private:
bool vertical = false;
- AlignMode align = ALIGN_BEGIN;
+ AlignmentMode alignment = ALIGNMENT_BEGIN;
void _resort();
@@ -57,8 +57,8 @@ protected:
public:
Control *add_spacer(bool p_begin = false);
- void set_alignment(AlignMode p_align);
- AlignMode get_alignment() const;
+ void set_alignment(AlignmentMode p_alignment);
+ AlignmentMode get_alignment() const;
virtual Size2 get_minimum_size() const override;
@@ -84,6 +84,6 @@ public:
BoxContainer(true) {}
};
-VARIANT_ENUM_CAST(BoxContainer::AlignMode);
+VARIANT_ENUM_CAST(BoxContainer::AlignmentMode);
#endif // BOX_CONTAINER_H
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index 9818c8f0cc..552dd28513 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,9 +50,9 @@ Size2 Button::get_minimum_size() const {
if (!_icon.is_null()) {
minsize.height = MAX(minsize.height, _icon->get_height());
- if (icon_align != ALIGN_CENTER) {
+ if (icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
minsize.width += _icon->get_width();
- if (xl_text != "") {
+ if (!xl_text.is_empty()) {
minsize.width += get_theme_constant(SNAME("hseparation"));
}
} else {
@@ -82,13 +82,13 @@ void Button::_notification(int p_what) {
xl_text = atr(text);
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_DRAW: {
@@ -126,7 +126,8 @@ void Button::_notification(int p_what) {
}
} break;
case DRAW_HOVER_PRESSED: {
- if (has_theme_stylebox(SNAME("hover_pressed")) && has_theme_stylebox_override("hover_pressed")) {
+ // Edge case for CheckButton and CheckBox.
+ if (has_theme_stylebox("hover_pressed")) {
if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
style = get_theme_stylebox(SNAME("hover_pressed_mirrored"));
} else {
@@ -138,8 +139,6 @@ void Button::_notification(int p_what) {
}
if (has_theme_color(SNAME("font_hover_pressed_color"))) {
color = get_theme_color(SNAME("font_hover_pressed_color"));
- } else {
- color = get_theme_color(SNAME("font_color"));
}
if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
color_icon = get_theme_color(SNAME("icon_hover_pressed_color"));
@@ -216,19 +215,19 @@ void Button::_notification(int p_what) {
}
Rect2 icon_region = Rect2();
- TextAlign icon_align_rtl_checked = icon_align;
- TextAlign align_rtl_checked = align;
+ HorizontalAlignment icon_align_rtl_checked = icon_alignment;
+ HorizontalAlignment align_rtl_checked = alignment;
// Swap icon and text alignment sides if right-to-left layout is set.
if (rtl) {
- if (icon_align == ALIGN_RIGHT) {
- icon_align_rtl_checked = ALIGN_LEFT;
- } else if (icon_align == ALIGN_LEFT) {
- icon_align_rtl_checked = ALIGN_RIGHT;
+ if (icon_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
+ icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
+ } else if (icon_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
+ icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
}
- if (align == ALIGN_RIGHT) {
- align_rtl_checked = ALIGN_LEFT;
- } else if (align == ALIGN_LEFT) {
- align_rtl_checked = ALIGN_RIGHT;
+ if (alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
+ align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
+ } else if (alignment == HORIZONTAL_ALIGNMENT_LEFT) {
+ align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
}
}
if (!_icon.is_null()) {
@@ -240,14 +239,14 @@ void Button::_notification(int p_what) {
float icon_ofs_region = 0.0;
Point2 style_offset;
Size2 icon_size = _icon->get_size();
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
style_offset.x = style->get_margin(SIDE_LEFT);
if (_internal_margin[SIDE_LEFT] > 0) {
icon_ofs_region = _internal_margin[SIDE_LEFT] + get_theme_constant(SNAME("hseparation"));
}
- } else if (icon_align_rtl_checked == ALIGN_CENTER) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
style_offset.x = 0.0;
- } else if (icon_align_rtl_checked == ALIGN_RIGHT) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
style_offset.x = -style->get_margin(SIDE_RIGHT);
if (_internal_margin[SIDE_RIGHT] > 0) {
icon_ofs_region = -_internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation"));
@@ -258,7 +257,7 @@ void Button::_notification(int p_what) {
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
_size.width -= get_theme_constant(SNAME("hseparation")) + icon_ofs_region;
- if (!clip_text && icon_align_rtl_checked != ALIGN_CENTER) {
+ if (!clip_text && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
_size.width -= text_buf->get_size().width;
}
float icon_width = _icon->get_width() * _size.height / _icon->get_height();
@@ -272,9 +271,9 @@ void Button::_notification(int p_what) {
icon_size = Size2(icon_width, icon_height);
}
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
- } else if (icon_align_rtl_checked == ALIGN_CENTER) {
+ } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), Math::floor((valign - icon_size.y) * 0.5)), icon_size);
} else {
icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, Math::floor((valign - icon_size.y) * 0.5)), icon_size);
@@ -286,7 +285,7 @@ void Button::_notification(int p_what) {
}
Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_theme_constant(SNAME("hseparation")), 0) : Point2();
- if (align_rtl_checked == ALIGN_CENTER && icon_align_rtl_checked == ALIGN_CENTER) {
+ if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
icon_ofs.x = 0.0;
}
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
@@ -303,9 +302,12 @@ void Button::_notification(int p_what) {
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
+ text_buf->set_alignment(align_rtl_checked);
+ text_buf->set_width(text_width);
switch (align_rtl_checked) {
- case ALIGN_LEFT: {
- if (icon_align_rtl_checked != ALIGN_LEFT) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
+ if (icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_LEFT) {
icon_ofs.x = 0.0;
}
if (_internal_margin[SIDE_LEFT] > 0) {
@@ -315,23 +317,23 @@ void Button::_notification(int p_what) {
}
text_ofs.y += style->get_offset().y;
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (text_ofs.x < 0) {
text_ofs.x = 0;
}
- if (icon_align_rtl_checked == ALIGN_LEFT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
text_ofs += icon_ofs;
}
text_ofs += style->get_offset();
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (_internal_margin[SIDE_RIGHT] > 0) {
text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - get_theme_constant(SNAME("hseparation"));
} else {
text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width;
}
text_ofs.y += style->get_offset().y;
- if (icon_align_rtl_checked == ALIGN_RIGHT) {
+ if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
text_ofs.x -= icon_ofs.x;
}
} break;
@@ -358,7 +360,7 @@ void Button::_shape() {
} else {
text_buf->set_direction((TextServer::Direction)text_direction);
}
- text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
void Button::set_text(const String &p_text) {
@@ -368,7 +370,7 @@ void Button::set_text(const String &p_text) {
_shape();
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -428,7 +430,7 @@ void Button::set_icon(const Ref<Texture2D> &p_icon) {
if (icon != p_icon) {
icon = p_icon;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -440,7 +442,7 @@ void Button::set_expand_icon(bool p_enabled) {
if (expand_icon != p_enabled) {
expand_icon = p_enabled;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -463,7 +465,7 @@ void Button::set_clip_text(bool p_enabled) {
if (clip_text != p_enabled) {
clip_text = p_enabled;
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -471,25 +473,25 @@ bool Button::get_clip_text() const {
return clip_text;
}
-void Button::set_text_align(TextAlign p_align) {
- if (align != p_align) {
- align = p_align;
+void Button::set_text_alignment(HorizontalAlignment p_alignment) {
+ if (alignment != p_alignment) {
+ alignment = p_alignment;
update();
}
}
-Button::TextAlign Button::get_text_align() const {
- return align;
+HorizontalAlignment Button::get_text_alignment() const {
+ return alignment;
}
-void Button::set_icon_align(TextAlign p_align) {
- icon_align = p_align;
- minimum_size_changed();
+void Button::set_icon_alignment(HorizontalAlignment p_alignment) {
+ icon_alignment = p_alignment;
+ update_minimum_size();
update();
}
-Button::TextAlign Button::get_icon_align() const {
- return icon_align;
+HorizontalAlignment Button::get_icon_alignment() const {
+ return icon_alignment;
}
bool Button::_set(const StringName &p_name, const Variant &p_value) {
@@ -497,7 +499,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -505,7 +507,7 @@ bool Button::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -537,7 +539,7 @@ bool Button::_get(const StringName &p_name, Variant &r_ret) const {
void Button::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -558,25 +560,21 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat);
ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text);
- ClassDB::bind_method(D_METHOD("set_text_align", "align"), &Button::set_text_align);
- ClassDB::bind_method(D_METHOD("get_text_align"), &Button::get_text_align);
- ClassDB::bind_method(D_METHOD("set_icon_align", "icon_align"), &Button::set_icon_align);
- ClassDB::bind_method(D_METHOD("get_icon_align"), &Button::get_icon_align);
+ ClassDB::bind_method(D_METHOD("set_text_alignment", "alignment"), &Button::set_text_alignment);
+ ClassDB::bind_method(D_METHOD("get_text_alignment"), &Button::get_text_alignment);
+ ClassDB::bind_method(D_METHOD("set_icon_alignment", "icon_alignment"), &Button::set_icon_alignment);
+ ClassDB::bind_method(D_METHOD("get_icon_alignment"), &Button::get_icon_alignment);
ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon);
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
-
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
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"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_align", "get_icon_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
}
diff --git a/scene/gui/button.h b/scene/gui/button.h
index fd36cb77af..1abf86c986 100644
--- a/scene/gui/button.h
+++ b/scene/gui/button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,13 +37,6 @@
class Button : public BaseButton {
GDCLASS(Button, BaseButton);
-public:
- enum TextAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
- };
-
private:
bool flat = false;
String text;
@@ -57,8 +50,8 @@ private:
Ref<Texture2D> icon;
bool expand_icon = false;
bool clip_text = false;
- TextAlign align = ALIGN_CENTER;
- TextAlign icon_align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_CENTER;
+ HorizontalAlignment icon_alignment = HORIZONTAL_ALIGNMENT_LEFT;
float _internal_margin[4] = {};
void _shape();
@@ -100,16 +93,14 @@ public:
void set_clip_text(bool p_enabled);
bool get_clip_text() const;
- void set_text_align(TextAlign p_align);
- TextAlign get_text_align() const;
+ void set_text_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_text_alignment() const;
- void set_icon_align(TextAlign p_align);
- TextAlign get_icon_align() const;
+ void set_icon_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_icon_alignment() const;
Button(const String &p_text = String());
~Button();
};
-VARIANT_ENUM_CAST(Button::TextAlign);
-
#endif
diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp
index 909516e7ef..f3306783f3 100644
--- a/scene/gui/center_container.cpp
+++ b/scene/gui/center_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -61,7 +61,7 @@ void CenterContainer::set_use_top_left(bool p_enable) {
use_top_left = p_enable;
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h
index 0944f200fc..16a10c8070 100644
--- a/scene/gui/center_container.h
+++ b/scene/gui/center_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp
index 411fb2e1f0..da2d4369d1 100644
--- a/scene/gui/check_box.cpp
+++ b/scene/gui/check_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -123,7 +123,7 @@ CheckBox::CheckBox(const String &p_text) :
Button(p_text) {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+ set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (is_layout_rtl()) {
_set_internal_margin(SIDE_RIGHT, get_icon_size().width);
diff --git a/scene/gui/check_box.h b/scene/gui/check_box.h
index 9fb0aea218..fcdb2ce08c 100644
--- a/scene/gui/check_box.h
+++ b/scene/gui/check_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,9 +32,7 @@
#define CHECK_BOX_H
#include "scene/gui/button.h"
-/**
-@author Mariano Suligoy <marianognu.esyrpg@gmail.com>
-*/
+
class CheckBox : public Button {
GDCLASS(CheckBox, Button);
@@ -50,4 +48,4 @@ public:
~CheckBox();
};
-#endif
+#endif // CHECK_BOX_H
diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp
index 162a256d23..afb23a540b 100644
--- a/scene/gui/check_button.cpp
+++ b/scene/gui/check_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -107,7 +107,7 @@ void CheckButton::_notification(int p_what) {
CheckButton::CheckButton() {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+ 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 29c557ce89..9a72d04db2 100644
--- a/scene/gui/check_button.h
+++ b/scene/gui/check_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -32,9 +32,7 @@
#define CHECK_BUTTON_H
#include "scene/gui/button.h"
-/**
-@author Juan Linietsky <reduzio@gmail.com>
-*/
+
class CheckButton : public Button {
GDCLASS(CheckButton, Button);
@@ -48,4 +46,4 @@ public:
~CheckButton();
};
-#endif
+#endif // CHECK_BUTTON_H
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index a8c5966569..b3754ba6db 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -92,7 +92,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 = (int)font->get_char_size('0', 0, font_size).width;
+ const int char_size = Math::round(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();
@@ -143,7 +143,6 @@ void CodeEdit::_notification(int p_what) {
code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color);
- draw_rect(Rect2(code_completion_rect.position + Vector2(icon_area_size.x + icon_hsep, 0), Size2(MIN(code_completion_base_width, code_completion_rect.size.width - (icon_area_size.x + icon_hsep)), code_completion_rect.size.height)), code_completion_existing_color);
for (int i = 0; i < lines; i++) {
int l = code_completion_line_ofs + i;
@@ -170,13 +169,24 @@ void CodeEdit::_notification(int p_what) {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
draw_rect(Rect2(Point2(code_completion_rect.position.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
}
- tl->set_align(HALIGN_RIGHT);
+ tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
if (code_completion_options[l].default_value.get_type() == Variant::COLOR) {
draw_rect(Rect2(Point2(code_completion_rect.position.x + code_completion_rect.size.width - icon_area_size.x, icon_area.position.y), icon_area_size), (Color)code_completion_options[l].default_value);
}
- tl->set_align(HALIGN_LEFT);
+ tl->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
+
+ Point2 match_pos = Point2(code_completion_rect.position.x + icon_area_size.x + icon_hsep, code_completion_rect.position.y + i * row_height);
+
+ for (int j = 0; j < code_completion_options[l].matches.size(); j++) {
+ Pair<int, int> match = code_completion_options[l].matches[j];
+ int match_offset = font->get_string_size(code_completion_options[l].display.substr(0, match.first), font_size).width;
+ int match_len = font->get_string_size(code_completion_options[l].display.substr(match.first, match.second), font_size).width;
+
+ draw_rect(Rect2(match_pos + Point2(match_offset, 0), Size2(match_len, row_height)), code_completion_existing_color);
+ }
+
tl->draw(ci, title_pos, code_completion_options[l].font_color);
}
@@ -189,7 +199,7 @@ void CodeEdit::_notification(int p_what) {
}
/* Code hint */
- if (caret_visible && code_hint != "" && (!code_completion_active || (code_completion_below != code_hint_draw_below))) {
+ if (caret_visible && !code_hint.is_empty() && (!code_completion_active || (code_completion_below != code_hint_draw_below))) {
const int font_height = font->get_height(font_size);
Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"), SNAME("TooltipPanel"));
Color font_color = get_theme_color(SNAME("font_color"), SNAME("TooltipLabel"));
@@ -229,7 +239,7 @@ void CodeEdit::_notification(int p_what) {
Point2 round_ofs = hint_ofs + sb->get_offset() + Vector2(0, font->get_ascent(font_size) + font_height * i + yofs);
round_ofs = round_ofs.round();
- draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HALIGN_LEFT, -1, font_size, font_color);
+ draw_string(font, round_ofs, line.replace(String::chr(0xFFFF), ""), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, font_color);
if (end > 0) {
// Draw an underline for the currently edited function parameter.
const Vector2 b = hint_ofs + sb->get_offset() + Vector2(begin, font_height + font_height * i + yofs);
@@ -315,7 +325,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
} else {
if (mb->get_button_index() == MouseButton::LEFT) {
- if (mb->is_command_pressed() && symbol_lookup_word != String()) {
+ if (mb->is_command_pressed() && !symbol_lookup_word.is_empty()) {
Vector2i mpos = mb->get_position();
if (is_layout_rtl()) {
mpos.x = get_size().x - mpos.x;
@@ -530,7 +540,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
/* General overrides */
Control::CursorShape CodeEdit::get_cursor_shape(const Point2 &p_pos) const {
- if (symbol_lookup_word != String()) {
+ if (!symbol_lookup_word.is_empty()) {
return CURSOR_POINTING_HAND;
}
@@ -2075,8 +2085,6 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_auto_brace_completion_close_key", "open_key"), &CodeEdit::get_auto_brace_completion_close_key);
/* Main Gutter */
- ClassDB::bind_method(D_METHOD("_main_gutter_draw_callback"), &CodeEdit::_main_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_breakpoints_gutter", "enable"), &CodeEdit::set_draw_breakpoints_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_breakpoints_gutter"), &CodeEdit::is_drawing_breakpoints_gutter);
@@ -2105,16 +2113,12 @@ void CodeEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_executing_lines"), &CodeEdit::get_executing_lines);
/* Line numbers */
- ClassDB::bind_method(D_METHOD("_line_number_draw_callback"), &CodeEdit::_line_number_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_line_numbers", "enable"), &CodeEdit::set_draw_line_numbers);
ClassDB::bind_method(D_METHOD("is_draw_line_numbers_enabled"), &CodeEdit::is_draw_line_numbers_enabled);
ClassDB::bind_method(D_METHOD("set_line_numbers_zero_padded", "enable"), &CodeEdit::set_line_numbers_zero_padded);
ClassDB::bind_method(D_METHOD("is_line_numbers_zero_padded"), &CodeEdit::is_line_numbers_zero_padded);
/* Fold Gutter */
- ClassDB::bind_method(D_METHOD("_fold_gutter_draw_callback"), &CodeEdit::_fold_gutter_draw_callback);
-
ClassDB::bind_method(D_METHOD("set_draw_fold_gutter", "enable"), &CodeEdit::set_draw_fold_gutter);
ClassDB::bind_method(D_METHOD("is_drawing_fold_gutter"), &CodeEdit::is_drawing_fold_gutter);
@@ -2597,7 +2601,7 @@ void CodeEdit::_add_delimiter(const String &p_start_key, const String &p_end_key
delimiter.type = p_type;
delimiter.start_key = p_start_key;
delimiter.end_key = p_end_key;
- delimiter.line_only = p_line_only || p_end_key == "";
+ delimiter.line_only = p_line_only || p_end_key.is_empty();
delimiters.insert(at, delimiter);
if (!setting_delimiters) {
delimiter_cache.clear();
@@ -2647,7 +2651,7 @@ void CodeEdit::_set_delimiters(const TypedArray<String> &p_delimiters, Delimiter
const String start_key = key.get_slice(" ", 0);
const String end_key = key.get_slice_count(" ") > 1 ? key.get_slice(" ", 1) : String();
- _add_delimiter(start_key, end_key, end_key == "", p_type);
+ _add_delimiter(start_key, end_key, end_key.is_empty(), p_type);
}
setting_delimiters = false;
_update_delimiter_cache();
@@ -2808,6 +2812,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
code_completion_base = string_to_complete;
Vector<ScriptCodeCompletionOption> completion_options_casei;
+ Vector<ScriptCodeCompletionOption> completion_options_substr;
+ Vector<ScriptCodeCompletionOption> completion_options_substr_casei;
Vector<ScriptCodeCompletionOption> completion_options_subseq;
Vector<ScriptCodeCompletionOption> completion_options_subseq_casei;
@@ -2862,35 +2868,100 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
const char32_t *tgt = &option.display[0];
const char32_t *tgt_lower = &display_lower[0];
- const char32_t *ssq_last_tgt = nullptr;
- const char32_t *ssq_lower_last_tgt = nullptr;
+ const char32_t *sst = &string_to_complete[0];
+ const char32_t *sst_lower = &display_lower[0];
+
+ Vector<Pair<int, int>> ssq_matches;
+ int ssq_match_start = 0;
+ int ssq_match_len = 0;
+
+ Vector<Pair<int, int>> ssq_lower_matches;
+ int ssq_lower_match_start = 0;
+ int ssq_lower_match_len = 0;
+
+ int sst_start = -1;
+ int sst_lower_start = -1;
+
+ for (int i = 0; *tgt; tgt++, tgt_lower++, i++) {
+ // Check substring.
+ if (*sst == *tgt) {
+ sst++;
+ if (sst_start == -1) {
+ sst_start = i;
+ }
+ } else if (sst_start != -1 && *sst) {
+ sst = &string_to_complete[0];
+ sst_start = -1;
+ }
- for (; *tgt; tgt++, tgt_lower++) {
+ // Check subsequence.
if (*ssq == *tgt) {
ssq++;
- ssq_last_tgt = tgt;
+ if (ssq_match_len == 0) {
+ ssq_match_start = i;
+ }
+ ssq_match_len++;
+ } else if (ssq_match_len > 0) {
+ ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len));
+ ssq_match_len = 0;
+ }
+
+ // Check lower substring.
+ if (*sst_lower == *tgt) {
+ sst_lower++;
+ if (sst_lower_start == -1) {
+ sst_lower_start = i;
+ }
+ } else if (sst_lower_start != -1 && *sst_lower) {
+ sst_lower = &string_to_complete[0];
+ sst_lower_start = -1;
}
+
+ // Check lower subsequence.
if (*ssq_lower == *tgt_lower) {
ssq_lower++;
- ssq_lower_last_tgt = tgt;
+ if (ssq_lower_match_len == 0) {
+ ssq_lower_match_start = i;
+ }
+ ssq_lower_match_len++;
+ } else if (ssq_lower_match_len > 0) {
+ ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len));
+ ssq_lower_match_len = 0;
}
}
/* Matched the whole subsequence in s. */
- if (!*ssq) {
- /* Finished matching in the first s.length() characters. */
- if (ssq_last_tgt == &option.display[string_to_complete.length() - 1]) {
+ if (!*ssq) { // Matched the whole subsequence in s.
+ option.matches.clear();
+
+ if (sst_start == 0) { // Matched substring in beginning of s.
+ option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length()));
code_completion_options.push_back(option);
+ } else if (sst_start > 0) { // Matched substring in s.
+ option.matches.push_back(Pair<int, int>(sst_start, string_to_complete.length()));
+ completion_options_substr.push_back(option);
} else {
+ if (ssq_match_len > 0) {
+ ssq_matches.push_back(Pair<int, int>(ssq_match_start, ssq_match_len));
+ }
+ option.matches.append_array(ssq_matches);
completion_options_subseq.push_back(option);
}
max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
- /* Matched the whole subsequence in s_lower. */
- } else if (!*ssq_lower) {
- /* Finished matching in the first s.length() characters. */
- if (ssq_lower_last_tgt == &option.display[string_to_complete.length() - 1]) {
+ } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower.
+ option.matches.clear();
+
+ if (sst_lower_start == 0) { // Matched substring in beginning of s_lower.
+ option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length()));
completion_options_casei.push_back(option);
+ } else if (sst_lower_start > 0) { // Matched substring in s_lower.
+ option.matches.push_back(Pair<int, int>(sst_lower_start, string_to_complete.length()));
+ completion_options_substr_casei.push_back(option);
} else {
+ if (ssq_lower_match_len > 0) {
+ ssq_lower_matches.push_back(Pair<int, int>(ssq_lower_match_start, ssq_lower_match_len));
+ }
+ option.matches.append_array(ssq_lower_matches);
completion_options_subseq_casei.push_back(option);
}
max_width = MAX(max_width, font->get_string_size(option.display, font_size).width + offset);
@@ -2990,7 +3061,7 @@ CodeEdit::CodeEdit() {
add_auto_brace_completion_pair("\"", "\"");
add_auto_brace_completion_pair("\'", "\'");
- /* Delimiter traking */
+ /* Delimiter tracking */
add_string_delimiter("\"", "\"", false);
add_string_delimiter("\'", "\'", false);
@@ -3007,7 +3078,7 @@ CodeEdit::CodeEdit() {
set_gutter_draw(gutter_idx, false);
set_gutter_overwritable(gutter_idx, true);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_main_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_main_gutter_draw_callback));
gutter_idx++;
/* Line numbers */
@@ -3015,7 +3086,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "line_numbers");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_line_number_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_line_number_draw_callback));
gutter_idx++;
/* Fold Gutter */
@@ -3023,7 +3094,7 @@ CodeEdit::CodeEdit() {
set_gutter_name(gutter_idx, "fold_gutter");
set_gutter_draw(gutter_idx, false);
set_gutter_type(gutter_idx, GUTTER_TYPE_CUSTOM);
- set_gutter_custom_draw(gutter_idx, this, "_fold_gutter_draw_callback");
+ set_gutter_custom_draw(gutter_idx, callable_mp(this, &CodeEdit::_fold_gutter_draw_callback));
gutter_idx++;
connect("lines_edited_from", callable_mp(this, &CodeEdit::_lines_edited_from));
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index f0d971dd35..cb1309ced3 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 5a378554c9..36ea843d1e 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -1274,7 +1274,7 @@ ColorPicker::ColorPicker() :
preset_container->set_columns(preset_column_count);
add_child(preset_container, false, INTERNAL_MODE_FRONT);
- btn_add_preset->set_icon_align(Button::ALIGN_CENTER);
+ btn_add_preset->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
btn_add_preset->set_tooltip(RTR("Add current color as a preset."));
btn_add_preset->connect("pressed", callable_mp(this, &ColorPicker::_add_preset_pressed));
preset_container->add_child(btn_add_preset);
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index ad4f5ad5b1..d6067b1cf4 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp
index e35d37d520..dbac1fc78a 100644
--- a/scene/gui/color_rect.cpp
+++ b/scene/gui/color_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/color_rect.h b/scene/gui/color_rect.h
index 5c650f9f01..35c8ebcaf8 100644
--- a/scene/gui/color_rect.h
+++ b/scene/gui/color_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp
index a1bd82f6f7..7b213ec314 100644
--- a/scene/gui/container.cpp
+++ b/scene/gui/container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,7 +35,7 @@
void Container::_child_minsize_changed() {
//Size2 ms = get_combined_minimum_size();
//if (ms.width > get_size().width || ms.height > get_size().height) {
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -51,7 +51,7 @@ void Container::add_child_notify(Node *p_child) {
control->connect(SNAME("minimum_size_changed"), callable_mp(this, &Container::_child_minsize_changed));
control->connect(SNAME("visibility_changed"), callable_mp(this, &Container::_child_minsize_changed));
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -62,7 +62,7 @@ void Container::move_child_notify(Node *p_child) {
return;
}
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
@@ -78,7 +78,7 @@ void Container::remove_child_notify(Node *p_child) {
control->disconnect("minimum_size_changed", callable_mp(this, &Container::_child_minsize_changed));
control->disconnect("visibility_changed", callable_mp(this, &Container::_child_minsize_changed));
- minimum_size_changed();
+ update_minimum_size();
queue_sort();
}
diff --git a/scene/gui/container.h b/scene/gui/container.h
index f3ae948556..0e986f46ef 100644
--- a/scene/gui/container.h
+++ b/scene/gui/container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 9f715be155..f167062f7d 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -192,7 +192,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) {
return;
}
data.custom_minimum_size = p_custom;
- minimum_size_changed();
+ update_minimum_size();
}
Size2 Control::get_custom_minimum_size() const {
@@ -213,7 +213,7 @@ void Control::_update_minimum_size_cache() {
data.minimum_size_valid = true;
if (size_changed) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -589,6 +589,7 @@ void Control::_notification(int p_notification) {
_size_changed();
} break;
case NOTIFICATION_EXIT_TREE: {
+ release_focus();
get_viewport()->_gui_remove_control(this);
} break;
case NOTIFICATION_READY: {
@@ -713,7 +714,7 @@ void Control::_notification(int p_notification) {
update();
} break;
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
@@ -732,8 +733,10 @@ void Control::_notification(int p_notification) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
- data.is_rtl_dirty = true;
- _size_changed();
+ if (is_inside_tree()) {
+ data.is_rtl_dirty = true;
+ _size_changed();
+ }
} break;
}
}
@@ -1148,12 +1151,12 @@ float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_base_scale()) {
- return theme_owner->data.theme->get_default_theme_base_scale();
+ if (theme_owner && theme_owner->data.theme->has_default_base_scale()) {
+ return theme_owner->data.theme->get_default_base_scale();
}
- if (theme_owner_window && theme_owner_window->theme->has_default_theme_base_scale()) {
- return theme_owner_window->theme->get_default_theme_base_scale();
+ if (theme_owner_window && theme_owner_window->theme->has_default_base_scale()) {
+ return theme_owner_window->theme->get_default_base_scale();
}
Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
@@ -1175,13 +1178,16 @@ float Control::fetch_theme_default_base_scale(Control *p_theme_owner, Window *p_
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_base_scale()) {
- return Theme::get_project_default()->get_default_theme_base_scale();
+ if (Theme::get_project_default()->has_default_base_scale()) {
+ return Theme::get_project_default()->get_default_base_scale();
}
}
// Lastly, fall back on the default Theme.
- return Theme::get_default()->get_default_theme_base_scale();
+ if (Theme::get_default()->has_default_base_scale()) {
+ return Theme::get_default()->get_default_base_scale();
+ }
+ return Theme::get_fallback_base_scale();
}
float Control::get_theme_default_base_scale() const {
@@ -1196,12 +1202,12 @@ Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_th
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_font()) {
- return theme_owner->data.theme->get_default_theme_font();
+ if (theme_owner && theme_owner->data.theme->has_default_font()) {
+ return theme_owner->data.theme->get_default_font();
}
- if (theme_owner_window && theme_owner_window->theme->has_default_theme_font()) {
- return theme_owner_window->theme->get_default_theme_font();
+ if (theme_owner_window && theme_owner_window->theme->has_default_font()) {
+ return theme_owner_window->theme->get_default_font();
}
Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
@@ -1223,13 +1229,16 @@ Ref<Font> Control::fetch_theme_default_font(Control *p_theme_owner, Window *p_th
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_font()) {
- return Theme::get_project_default()->get_default_theme_font();
+ if (Theme::get_project_default()->has_default_font()) {
+ return Theme::get_project_default()->get_default_font();
}
}
// Lastly, fall back on the default Theme.
- return Theme::get_default()->get_default_theme_font();
+ if (Theme::get_default()->has_default_font()) {
+ return Theme::get_default()->get_default_font();
+ }
+ return Theme::get_fallback_font();
}
Ref<Font> Control::get_theme_default_font() const {
@@ -1244,12 +1253,12 @@ int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_the
Window *theme_owner_window = p_theme_owner_window;
while (theme_owner || theme_owner_window) {
- if (theme_owner && theme_owner->data.theme->has_default_theme_font_size()) {
- return theme_owner->data.theme->get_default_theme_font_size();
+ if (theme_owner && theme_owner->data.theme->has_default_font_size()) {
+ return theme_owner->data.theme->get_default_font_size();
}
- if (theme_owner_window && theme_owner_window->theme->has_default_theme_font_size()) {
- return theme_owner_window->theme->get_default_theme_font_size();
+ if (theme_owner_window && theme_owner_window->theme->has_default_font_size()) {
+ return theme_owner_window->theme->get_default_font_size();
}
Node *parent = theme_owner ? theme_owner->get_parent() : theme_owner_window->get_parent();
@@ -1271,13 +1280,16 @@ int Control::fetch_theme_default_font_size(Control *p_theme_owner, Window *p_the
// Secondly, check the project-defined Theme resource.
if (Theme::get_project_default().is_valid()) {
- if (Theme::get_project_default()->has_default_theme_font_size()) {
- return Theme::get_project_default()->get_default_theme_font_size();
+ if (Theme::get_project_default()->has_default_font_size()) {
+ return Theme::get_project_default()->get_default_font_size();
}
}
// Lastly, fall back on the default Theme.
- return Theme::get_default()->get_default_theme_font_size();
+ if (Theme::get_default()->has_default_font_size()) {
+ return Theme::get_default()->get_default_font_size();
+ }
+ return Theme::get_fallback_font_size();
}
int Control::get_theme_default_font_size() const {
@@ -1296,7 +1308,7 @@ Rect2 Control::get_parent_anchorable_rect() const {
#ifdef TOOLS_ENABLED
Node *edited_root = get_tree()->get_edited_scene_root();
if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
- parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
+ parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height"));
} else {
parent_rect = get_viewport()->get_visible_rect();
}
@@ -2534,7 +2546,7 @@ void Control::grab_click_focus() {
get_viewport()->_gui_grab_click_focus(this);
}
-void Control::minimum_size_changed() {
+void Control::update_minimum_size() {
if (!is_inside_tree() || data.block_minimum_size_adjust) {
return;
}
@@ -2597,7 +2609,7 @@ bool Control::is_text_field() const {
return false;
}
-Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const {
+Array Control::structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const {
Array ret;
switch (p_theme_type) {
case STRUCTURED_TEXT_URI: {
@@ -2696,7 +2708,7 @@ real_t Control::get_rotation() const {
void Control::_override_changed() {
notification(NOTIFICATION_THEME_CHANGED);
emit_signal(SceneStringNames::get_singleton()->theme_changed);
- minimum_size_changed(); // overrides are likely to affect minimum size
+ update_minimum_size(); // Overrides are likely to affect minimum size.
}
void Control::set_pivot_offset(const Vector2 &p_pivot) {
@@ -2791,7 +2803,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
TypedArray<String> Control::get_configuration_warnings() const {
TypedArray<String> warnings = Node::get_configuration_warnings();
- if (data.mouse_filter == MOUSE_FILTER_IGNORE && data.tooltip != "") {
+ if (data.mouse_filter == MOUSE_FILTER_IGNORE && !data.tooltip.is_empty()) {
warnings.push_back(TTR("The Hint Tooltip won't be displayed as the control's Mouse Filter is set to \"Ignore\". To solve this, set the Mouse Filter to \"Stop\" or \"Pass\"."));
}
@@ -2977,7 +2989,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("warp_mouse", "to_position"), &Control::warp_mouse);
- ClassDB::bind_method(D_METHOD("minimum_size_changed"), &Control::minimum_size_changed);
+ ClassDB::bind_method(D_METHOD("update_minimum_size"), &Control::update_minimum_size);
ClassDB::bind_method(D_METHOD("set_layout_direction", "direction"), &Control::set_layout_direction);
ClassDB::bind_method(D_METHOD("get_layout_direction"), &Control::get_layout_direction);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 1a94cc68a6..bf79f790e7 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -279,7 +279,7 @@ protected:
//virtual void _window_gui_input(InputEvent p_event);
- virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String p_text) const;
+ virtual Array structured_text_parser(StructuredTextParser p_theme_type, const Array &p_args, const String &p_text) const;
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -441,7 +441,7 @@ public:
void set_stretch_ratio(real_t p_ratio);
real_t get_stretch_ratio() const;
- void minimum_size_changed();
+ void update_minimum_size();
/* FOCUS */
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index f66d3f6835..1cbe3adb3c 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -242,7 +242,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
hbc->add_spacer(true);
}
- if (p_action != "") {
+ if (!p_action.is_empty()) {
button->connect("pressed", callable_mp(this, &AcceptDialog::_custom_action), varray(p_action));
}
@@ -251,7 +251,7 @@ Button *AcceptDialog::add_button(const String &p_text, bool p_right, const Strin
Button *AcceptDialog::add_cancel_button(const String &p_cancel) {
String c = p_cancel;
- if (p_cancel == "") {
+ if (p_cancel.is_empty()) {
c = TTRC("Cancel");
}
Button *b = swap_cancel_ok ? add_button(c, true) : add_button(c);
diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h
index 8e803a2a7c..1365b1df24 100644
--- a/scene/gui/dialogs.h
+++ b/scene/gui/dialogs.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 44853fc006..44ef641cb8 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,6 +42,17 @@ FileDialog::RegisterFunc FileDialog::unregister_func = nullptr;
void FileDialog::popup_file_dialog() {
popup_centered_clamped(Size2i(700, 500), 0.8f);
+ _focus_file_text();
+}
+
+void FileDialog::_focus_file_text() {
+ int lp = file->get_text().rfind(".");
+ if (lp != -1) {
+ file->select(0, lp);
+ if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
+ file->grab_focus();
+ }
+ }
}
VBoxContainer *FileDialog::get_vbox() {
@@ -473,6 +484,13 @@ void FileDialog::update_file_list() {
dir_access->list_dir_begin();
+ if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
+ message->hide();
+ } else {
+ message->set_text(TTRC("You don't have permission to access contents of this folder."));
+ message->show();
+ }
+
TreeItem *root = tree->create_item();
Ref<Texture2D> folder = vbox->get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
Ref<Texture2D> file_icon = vbox->get_theme_icon(SNAME("file"), SNAME("FileDialog"));
@@ -482,17 +500,11 @@ void FileDialog::update_file_list() {
List<String> dirs;
bool is_hidden;
- String item;
-
- if (dir_access->is_readable(dir_access->get_current_dir().utf8().get_data())) {
- message->hide();
- } else {
- message->set_text(TTRC("You don't have permission to access contents of this folder."));
- message->show();
- }
+ String item = dir_access->get_next();
- while ((item = dir_access->get_next()) != "") {
+ while (!item.is_empty()) {
if (item == "." || item == "..") {
+ item = dir_access->get_next();
continue;
}
@@ -505,6 +517,7 @@ void FileDialog::update_file_list() {
dirs.push_back(item);
}
}
+ item = dir_access->get_next();
}
dirs.sort_custom<NaturalNoCaseComparator>();
@@ -647,6 +660,7 @@ void FileDialog::clear_filters() {
}
void FileDialog::add_filter(const String &p_filter) {
+ ERR_FAIL_COND_MSG(p_filter.begins_with("."), "Filter must be \"filename.extension\", can't start with dot.");
filters.push_back(p_filter);
update_filters();
invalidate();
@@ -685,13 +699,7 @@ void FileDialog::set_current_file(const String &p_file) {
file->set_text(p_file);
update_dir();
invalidate();
- int lp = p_file.rfind(".");
- if (lp != -1) {
- file->select(0, lp);
- if (file->is_inside_tree() && !get_tree()->is_node_being_edited(file)) {
- file->grab_focus();
- }
- }
+ _focus_file_text();
}
void FileDialog::set_current_path(const String &p_path) {
@@ -998,8 +1006,8 @@ FileDialog::FileDialog() {
message = memnew(Label);
message->hide();
message->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- message->set_align(Label::ALIGN_CENTER);
- message->set_valign(Label::VALIGN_CENTER);
+ message->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ message->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
tree->add_child(message);
file_box = memnew(HBoxContainer);
diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h
index b5190bdab1..9f8bc02b2a 100644
--- a/scene/gui/file_dialog.h
+++ b/scene/gui/file_dialog.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -113,6 +113,8 @@ private:
void update_file_list();
void update_filters();
+ void _focus_file_text();
+
void _tree_multi_selected(Object *p_object, int p_cell, bool p_selected);
void _tree_selected();
diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp
new file mode 100644
index 0000000000..d1ac60b325
--- /dev/null
+++ b/scene/gui/flow_container.cpp
@@ -0,0 +1,252 @@
+/*************************************************************************/
+/* flow_container.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "scene/gui/container.h"
+
+#include "flow_container.h"
+
+struct _LineData {
+ int child_count = 0;
+ int min_line_height = 0;
+ int min_line_length = 0;
+ int stretch_avail = 0;
+ float stretch_ratio_total = 0;
+};
+
+void FlowContainer::_resort() {
+ int separation_horizontal = get_theme_constant(SNAME("hseparation"));
+ int separation_vertical = get_theme_constant(SNAME("vseparation"));
+
+ bool rtl = is_layout_rtl();
+
+ Map<Control *, Size2i> children_minsize_cache;
+
+ Vector<_LineData> lines_data;
+
+ Vector2i ofs;
+ int line_height = 0;
+ int line_length = 0;
+ float line_stretch_ratio_total = 0;
+ int current_container_size = vertical ? get_rect().size.y : get_rect().size.x;
+ int children_in_current_line = 0;
+
+ // First pass for line wrapping and minimum size calculation.
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible_in_tree()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+
+ Size2i child_msc = child->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ if (children_in_current_line > 0) {
+ ofs.y += separation_vertical;
+ }
+ if (ofs.y + child_msc.y > current_container_size) {
+ line_length = ofs.y - separation_vertical;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new column (vertical line).
+ ofs.x += line_height + separation_horizontal;
+ ofs.y = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.x);
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.y += child_msc.y;
+
+ } else { /* HORIZONTAL */
+ if (children_in_current_line > 0) {
+ ofs.x += separation_horizontal;
+ }
+ if (ofs.x + child_msc.x > current_container_size) {
+ line_length = ofs.x - separation_horizontal;
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Move in new line.
+ ofs.y += line_height + separation_vertical;
+ ofs.x = 0;
+ line_height = 0;
+ line_stretch_ratio_total = 0;
+ children_in_current_line = 0;
+ }
+
+ line_height = MAX(line_height, child_msc.y);
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ line_stretch_ratio_total += child->get_stretch_ratio();
+ }
+ ofs.x += child_msc.x;
+ }
+
+ children_minsize_cache[child] = child_msc;
+ children_in_current_line++;
+ }
+ line_length = vertical ? (ofs.y) : (ofs.x);
+ lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total });
+
+ // Second pass for in-line expansion and alignment.
+
+ int current_line_idx = 0;
+ int child_idx_in_line = 0;
+
+ ofs.x = 0;
+ ofs.y = 0;
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *child = Object::cast_to<Control>(get_child(i));
+ if (!child || !child->is_visible_in_tree()) {
+ continue;
+ }
+ if (child->is_set_as_top_level()) {
+ continue;
+ }
+ Size2i child_size = children_minsize_cache[child];
+
+ _LineData line_data = lines_data[current_line_idx];
+ if (child_idx_in_line >= lines_data[current_line_idx].child_count) {
+ current_line_idx++;
+ child_idx_in_line = 0;
+ if (vertical) {
+ ofs.x += line_data.min_line_height + separation_horizontal;
+ ofs.y = 0;
+ } else {
+ ofs.x = 0;
+ ofs.y += line_data.min_line_height + separation_vertical;
+ }
+ line_data = lines_data[current_line_idx];
+ }
+
+ if (vertical) { /* VERTICAL */
+ if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.width = line_data.min_line_height;
+ }
+
+ if (child->get_v_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.height += stretch;
+ }
+
+ } else { /* HORIZONTAL */
+ if (child->get_v_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) {
+ child_size.height = line_data.min_line_height;
+ }
+
+ if (child->get_h_size_flags() & SIZE_EXPAND) {
+ int stretch = line_data.stretch_avail * child->get_stretch_ratio() / line_data.stretch_ratio_total;
+ child_size.width += stretch;
+ }
+ }
+
+ Rect2 child_rect = Rect2(ofs, child_size);
+ if (rtl) {
+ child_rect.position.x = get_rect().size.x - child_rect.position.x - child_rect.size.width;
+ }
+
+ fit_child_in_rect(child, child_rect);
+
+ if (vertical) { /* VERTICAL */
+ ofs.y += child_size.height + separation_vertical;
+ } else { /* HORIZONTAL */
+ ofs.x += child_size.width + separation_horizontal;
+ }
+
+ child_idx_in_line++;
+ }
+ cached_size = (vertical ? ofs.x : ofs.y) + line_height;
+ cached_line_count = lines_data.size();
+}
+
+Size2 FlowContainer::get_minimum_size() const {
+ Size2i minimum;
+
+ 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;
+ }
+
+ if (!c->is_visible()) {
+ continue;
+ }
+
+ Size2i size = c->get_combined_minimum_size();
+
+ if (vertical) { /* VERTICAL */
+ minimum.height = MAX(minimum.height, size.height);
+ minimum.width = cached_size;
+
+ } else { /* HORIZONTAL */
+ minimum.width = MAX(minimum.width, size.width);
+ minimum.height = cached_size;
+ }
+ }
+
+ return minimum;
+}
+
+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();
+ } break;
+ }
+}
+
+int FlowContainer::get_line_count() const {
+ return cached_line_count;
+}
+
+FlowContainer::FlowContainer(bool p_vertical) {
+ vertical = p_vertical;
+}
+
+void FlowContainer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count);
+}
diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h
new file mode 100644
index 0000000000..e3ed423ae1
--- /dev/null
+++ b/scene/gui/flow_container.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* flow_container.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FLOW_CONTAINER_H
+#define FLOW_CONTAINER_H
+
+class Container;
+
+class FlowContainer : public Container {
+ GDCLASS(FlowContainer, Container);
+
+private:
+ int cached_size = 0;
+ int cached_line_count = 0;
+
+ bool vertical = false;
+
+ void _resort();
+
+protected:
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+ int get_line_count() const;
+
+ virtual Size2 get_minimum_size() const override;
+
+ FlowContainer(bool p_vertical = false);
+};
+
+class HFlowContainer : public FlowContainer {
+ GDCLASS(HFlowContainer, FlowContainer);
+
+public:
+ HFlowContainer() :
+ FlowContainer(false) {}
+};
+
+class VFlowContainer : public FlowContainer {
+ GDCLASS(VFlowContainer, FlowContainer);
+
+public:
+ VFlowContainer() :
+ FlowContainer(true) {}
+};
+
+#endif // FLOW_CONTAINER_H
diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 1210be15ce..bec3d58384 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -432,6 +432,10 @@ Gradient::InterpolationMode GradientEdit::get_interpolation_mode() {
return interpolation_mode;
}
+ColorPicker *GradientEdit::get_picker() {
+ return picker;
+}
+
void GradientEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("ramp_changed"));
}
diff --git a/scene/gui/gradient_edit.h b/scene/gui/gradient_edit.h
index 66b60d87c7..67531d4f4a 100644
--- a/scene/gui/gradient_edit.h
+++ b/scene/gui/gradient_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,7 +33,6 @@
#include "scene/gui/color_picker.h"
#include "scene/gui/popup.h"
-#include "scene/resources/default_theme/theme_data.h"
#include "scene/resources/gradient.h"
class GradientEdit : public Control {
@@ -75,6 +74,7 @@ public:
Vector<Gradient::Point> &get_points();
void set_interpolation_mode(Gradient::InterpolationMode p_interp_mode);
Gradient::InterpolationMode get_interpolation_mode();
+ ColorPicker *get_picker();
virtual Size2 get_minimum_size() const override;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index e7d98a686f..79b73f7cc3 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,6 +35,7 @@
#include "core/os/keyboard.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
+#include "scene/gui/view_panner.h"
constexpr int MINIMAP_OFFSET = 12;
constexpr int MINIMAP_PADDING = 5;
@@ -549,15 +550,13 @@ bool GraphEdit::_filter_input(const Point2 &p_point) {
}
for (int j = 0; j < gn->get_connection_output_count(); j++) {
- Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, false)) {
+ if (is_in_output_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
- Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, p_point / zoom, port_size, true)) {
+ if (is_in_input_hotzone(gn, j, p_point / zoom, port_size)) {
return true;
}
}
@@ -582,7 +581,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, click_pos, port_size, false)) {
+ if (is_in_output_hotzone(gn, j, click_pos, port_size)) {
if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) {
//check disconnect
for (const Connection &E : connections) {
@@ -602,6 +601,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
to = get_node(String(connecting_from)); //maybe it was erased
if (Object::cast_to<GraphNode>(to)) {
connecting = true;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false);
}
return;
}
@@ -618,13 +618,14 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
connecting_to = pos;
just_disconnected = false;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
return;
}
}
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
- if (is_in_hot_zone(pos / zoom, click_pos, port_size, true)) {
+ if (is_in_input_hotzone(gn, j, click_pos, port_size)) {
if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) {
//check disconnect
for (const Connection &E : connections) {
@@ -644,6 +645,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
fr = get_node(String(connecting_from)); //maybe it was erased
if (Object::cast_to<GraphNode>(fr)) {
connecting = true;
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, true);
}
return;
}
@@ -660,7 +662,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
connecting_target = false;
connecting_to = pos;
just_disconnected = false;
-
+ emit_signal(SNAME("connection_drag_started"), connecting_from, connecting_index, false);
return;
}
}
@@ -690,7 +692,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_output_count(); j++) {
Vector2 pos = gn->get_connection_output_position(j) + gn->get_position();
int type = gn->get_connection_output_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, false)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_output_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -702,7 +704,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
for (int j = 0; j < gn->get_connection_input_count(); j++) {
Vector2 pos = gn->get_connection_input_position(j) + gn->get_position();
int type = gn->get_connection_input_type(j);
- if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos / zoom, mpos, port_size, true)) {
+ if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_input_hotzone(gn, j, mpos, port_size)) {
connecting_target = true;
connecting_to = pos;
connecting_target_to = gn->get_name();
@@ -742,11 +744,9 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) {
}
}
- connecting = false;
- top_layer->update();
- minimap->update();
- update();
- connections_layer->update();
+ if (connecting) {
+ force_connection_drag_end();
+ }
}
}
@@ -773,25 +773,29 @@ bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos)
}
}
-bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
- if (p_left) {
- if (!Rect2(
- pos.x - p_port_size.x / 2 - port_grab_distance_horizontal,
- pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
- p_port_size.x + port_grab_distance_horizontal,
- p_port_size.y + port_grab_distance_vertical)
- .has_point(p_mouse_pos)) {
- return false;
- }
+bool GraphEdit::is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+ bool success;
+ if (GDVIRTUAL_CALL(_is_in_input_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ return success;
} else {
- if (!Rect2(
- pos.x - p_port_size.x / 2,
- pos.y - p_port_size.y / 2 - port_grab_distance_vertical / 2,
- p_port_size.x + port_grab_distance_horizontal,
- p_port_size.y + port_grab_distance_vertical)
- .has_point(p_mouse_pos)) {
- return false;
- }
+ Vector2 pos = p_graph_node->get_connection_input_position(p_slot_index) + p_graph_node->get_position();
+ return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, true);
+ }
+}
+
+bool GraphEdit::is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size) {
+ bool success;
+ if (GDVIRTUAL_CALL(_is_in_output_hotzone, p_graph_node, p_slot_index, p_mouse_pos, success)) {
+ return success;
+ } else {
+ Vector2 pos = p_graph_node->get_connection_output_position(p_slot_index) + p_graph_node->get_position();
+ return is_in_port_hotzone(pos / zoom, p_mouse_pos, p_port_size, false);
+ }
+}
+
+bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left) {
+ if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) {
+ return false;
}
for (int i = 0; i < get_child_count(); i++) {
@@ -1066,13 +1070,9 @@ void GraphEdit::set_selected(Node *p_child) {
void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
ERR_FAIL_COND(p_ev.is_null());
+ panner->gui_input(p_ev);
Ref<InputEventMouseMotion> mm = p_ev;
- if (mm.is_valid() && ((mm->get_button_mask() & MouseButton::MASK_MIDDLE) != MouseButton::NONE || ((mm->get_button_mask() & MouseButton::MASK_LEFT) != MouseButton::NONE && Input::get_singleton()->is_key_pressed(Key::SPACE)))) {
- Vector2i relative = Input::get_singleton()->warp_mouse_motion(mm, get_global_rect());
- h_scroll->set_value(h_scroll->get_value() - relative.x);
- v_scroll->set_value(v_scroll->get_value() - relative.y);
- }
if (mm.is_valid() && dragging) {
if (!moving_selection) {
@@ -1162,11 +1162,9 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
minimap->update();
} else {
if (connecting) {
- connecting = false;
- top_layer->update();
- minimap->update();
+ force_connection_drag_end();
} else {
- emit_signal(SNAME("popup_request"), b->get_global_position());
+ emit_signal(SNAME("popup_request"), get_screen_position() + b->get_position());
}
}
}
@@ -1326,22 +1324,6 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
top_layer->update();
minimap->update();
}
-
- int scroll_direction = (b->get_button_index() == MouseButton::WHEEL_DOWN) - (b->get_button_index() == MouseButton::WHEEL_UP);
- if (scroll_direction != 0) {
- if (b->is_ctrl_pressed()) {
- if (b->is_shift_pressed()) {
- // Horizontal scrolling.
- h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- } else {
- // Vertical scrolling.
- v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * b->get_factor() / 8) * scroll_direction);
- }
- } else {
- // Zooming.
- set_zoom_custom(scroll_direction < 0 ? zoom * zoom_step : zoom / zoom_step, b->get_position());
- }
- }
}
if (p_ev->is_pressed()) {
@@ -1372,6 +1354,23 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) {
}
}
+void GraphEdit::_scroll_callback(Vector2 p_scroll_vec) {
+ if (p_scroll_vec.x != 0) {
+ h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x));
+ } else {
+ v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y));
+ }
+}
+
+void GraphEdit::_pan_callback(Vector2 p_scroll_vec) {
+ h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x);
+ v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y);
+}
+
+void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin) {
+ set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin);
+}
+
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
for (Connection &E : connections) {
if (E.from == p_from && E.from_port == p_from_port && E.to == p_to && E.to_port == p_to_port) {
@@ -1394,6 +1393,26 @@ void GraphEdit::clear_connections() {
connections_layer->update();
}
+void GraphEdit::force_connection_drag_end() {
+ ERR_FAIL_COND_MSG(!connecting, "Drag end requested without active drag!");
+ connecting = false;
+ connecting_valid = false;
+ top_layer->update();
+ minimap->update();
+ update();
+ connections_layer->update();
+ emit_signal(SNAME("connection_drag_ended"));
+}
+
+void GraphEdit::set_panning_scheme(PanningScheme p_scheme) {
+ panning_scheme = p_scheme;
+ panner->set_control_scheme((ViewPanner::ControlScheme)p_scheme);
+}
+
+GraphEdit::PanningScheme GraphEdit::get_panning_scheme() const {
+ return panning_scheme;
+}
+
void GraphEdit::set_zoom(float p_zoom) {
set_zoom_custom(p_zoom, get_size() / 2);
}
@@ -1692,7 +1711,7 @@ int GraphEdit::_set_operations(SET_OPERATIONS p_operation, Set<StringName> &r_u,
r_u.insert(E->get());
}
}
- return r_v.size();
+ return r_u.size();
} break;
default:
break;
@@ -1787,8 +1806,8 @@ void GraphEdit::_horizontal_alignment(Dictionary &r_root, Dictionary &r_align, c
}
}
- int start = up.size() / 2;
- int end = up.size() % 2 ? start : start + 1;
+ int start = (up.size() - 1) / 2;
+ int end = (up.size() - 1) % 2 ? start + 1 : start;
for (int p = start; p <= end; p++) {
StringName Align = r_align[current_node];
if (Align == current_node && r < up[p].first) {
@@ -2107,7 +2126,7 @@ void GraphEdit::arrange_nodes() {
} while (u != E->get());
}
- //Compute horizontal co-ordinates individually for layers to get uniform gap
+ // Compute horizontal coordinates individually for layers to get uniform gap.
float start_from = origin.x;
float largest_node_size = 0.0f;
@@ -2165,6 +2184,7 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity);
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
+ ClassDB::bind_method(D_METHOD("force_connection_drag_end"), &GraphEdit::force_connection_drag_end);
ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs);
ClassDB::bind_method(D_METHOD("set_scroll_ofs", "ofs"), &GraphEdit::set_scroll_ofs);
@@ -2177,6 +2197,9 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_valid_connection_type", "from_type", "to_type"), &GraphEdit::is_valid_connection_type);
ClassDB::bind_method(D_METHOD("get_connection_line", "from", "to"), &GraphEdit::get_connection_line);
+ ClassDB::bind_method(D_METHOD("set_panning_scheme", "scheme"), &GraphEdit::set_panning_scheme);
+ ClassDB::bind_method(D_METHOD("get_panning_scheme"), &GraphEdit::get_panning_scheme);
+
ClassDB::bind_method(D_METHOD("set_zoom", "zoom"), &GraphEdit::set_zoom);
ClassDB::bind_method(D_METHOD("get_zoom"), &GraphEdit::get_zoom);
@@ -2216,6 +2239,8 @@ void GraphEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled);
ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
+ GDVIRTUAL_BIND(_is_in_input_hotzone, "graph_node", "slot_index", "mouse_position");
+ GDVIRTUAL_BIND(_is_in_output_hotzone, "graph_node", "slot_index", "mouse_position");
ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
@@ -2229,6 +2254,7 @@ void GraphEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset"), "set_scroll_ofs", "get_scroll_ofs");
ADD_PROPERTY(PropertyInfo(Variant::INT, "snap_distance"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_snap"), "set_use_snap", "is_using_snap");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme");
ADD_GROUP("Connection Lines", "connection_lines");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_thickness"), "set_connection_lines_thickness", "get_connection_lines_thickness");
@@ -2260,6 +2286,11 @@ void GraphEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("begin_node_move"));
ADD_SIGNAL(MethodInfo("end_node_move"));
ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "ofs")));
+ ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING, "from"), PropertyInfo(Variant::STRING, "slot"), PropertyInfo(Variant::BOOL, "is_output")));
+ ADD_SIGNAL(MethodInfo("connection_drag_ended"));
+
+ BIND_ENUM_CONSTANT(SCROLL_ZOOMS);
+ BIND_ENUM_CONSTANT(SCROLL_PANS);
}
GraphEdit::GraphEdit() {
@@ -2272,6 +2303,10 @@ GraphEdit::GraphEdit() {
// Allow zooming 4 times from the default zoom level.
zoom_max = (1 * Math::pow(zoom_step, 4));
+ panner.instantiate();
+ panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback));
+ panner->set_disable_rmb(true);
+
top_layer = memnew(GraphEditFilter(this));
add_child(top_layer, false, INTERNAL_MODE_BACK);
top_layer->set_mouse_filter(MOUSE_FILTER_PASS);
@@ -2312,7 +2347,7 @@ GraphEdit::GraphEdit() {
zoom_hb->add_child(zoom_label);
zoom_label->set_visible(false);
zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- zoom_label->set_align(Label::ALIGN_CENTER);
+ zoom_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
zoom_label->set_custom_minimum_size(Size2(48, 0));
_update_zoom_label();
diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h
index 6c11f9df6a..4e998d30a7 100644
--- a/scene/gui/graph_edit.h
+++ b/scene/gui/graph_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,6 +41,7 @@
#include "scene/gui/texture_rect.h"
class GraphEdit;
+class ViewPanner;
class GraphEditFilter : public Control {
GDCLASS(GraphEditFilter, Control);
@@ -103,6 +104,12 @@ public:
float activity = 0.0;
};
+ // Should be in sync with ControlScheme in ViewPanner.
+ enum PanningScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
private:
Label *zoom_label;
Button *zoom_minus;
@@ -122,6 +129,11 @@ private:
float port_grab_distance_horizontal = 0.0;
float port_grab_distance_vertical;
+ Ref<ViewPanner> panner;
+ void _scroll_callback(Vector2 p_scroll_vec);
+ void _pan_callback(Vector2 p_scroll_vec);
+ void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin);
+
bool connecting = false;
String connecting_from;
bool connecting_out = false;
@@ -136,6 +148,7 @@ private:
bool connecting_valid = false;
Vector2 click_pos;
+ PanningScheme panning_scheme = SCROLL_ZOOMS;
bool dragging = false;
bool just_selected = false;
bool moving_selection = false;
@@ -183,7 +196,9 @@ private:
GraphEditMinimap *minimap;
void _top_layer_input(const Ref<InputEvent> &p_ev);
- bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
+ bool is_in_input_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_output_hotzone(GraphNode *p_graph_node, int p_slot_index, const Vector2 &p_mouse_pos, const Vector2i &p_port_size);
+ bool is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left);
void _top_layer_draw();
void _connections_layer_draw();
@@ -259,12 +274,15 @@ protected:
void _notification(int p_what);
GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2)
+ GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2)
+ GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2)
public:
Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
void clear_connections();
+ void force_connection_drag_end();
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
@@ -272,6 +290,9 @@ public:
void remove_valid_connection_type(int p_type, int p_with_type);
bool is_valid_connection_type(int p_type, int p_with_type) const;
+ void set_panning_scheme(PanningScheme p_scheme);
+ PanningScheme get_panning_scheme() const;
+
void set_zoom(float p_zoom);
void set_zoom_custom(float p_zoom, const Vector2 &p_center);
float get_zoom() const;
@@ -333,4 +354,6 @@ public:
GraphEdit();
};
+VARIANT_ENUM_CAST(GraphEdit::PanningScheme);
+
#endif // GRAPHEdit_H
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index e7094c89b1..30f6cf4a14 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -46,7 +46,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -54,7 +54,7 @@ bool GraphNode::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -153,7 +153,7 @@ bool GraphNode::_get(const StringName &p_name, Variant &r_ret) const {
void GraphNode::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
@@ -442,7 +442,7 @@ void GraphNode::_notification(int p_what) {
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
}
@@ -458,7 +458,7 @@ void GraphNode::_shape() {
} else {
title_buf->set_direction((TextServer::Direction)text_direction);
}
- title_buf->add_string(title, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ title_buf->add_string(title, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
#ifdef TOOLS_ENABLED
@@ -666,7 +666,7 @@ void GraphNode::set_title(const String &p_title) {
_shape();
update();
- minimum_size_changed();
+ update_minimum_size();
}
String GraphNode::get_title() const {
@@ -1022,7 +1022,7 @@ void GraphNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
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"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resizable"), "set_resizable", "is_resizable");
diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h
index 2238cfdb56..b41fc7f5d4 100644
--- a/scene/gui/graph_node.h
+++ b/scene/gui/graph_node.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp
index 2beb2624d2..465c9dac78 100644
--- a/scene/gui/grid_container.cpp
+++ b/scene/gui/grid_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -182,7 +182,7 @@ void GridContainer::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
@@ -195,7 +195,7 @@ void GridContainer::set_columns(int p_columns) {
ERR_FAIL_COND(p_columns < 1);
columns = p_columns;
queue_sort();
- minimum_size_changed();
+ update_minimum_size();
}
int GridContainer::get_columns() const {
diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h
index 9b43a5bc7e..9d77f90ab3 100644
--- a/scene/gui/grid_container.h
+++ b/scene/gui/grid_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp
index 408ef53e89..0cb3249c1d 100644
--- a/scene/gui/item_list.cpp
+++ b/scene/gui/item_list.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -43,7 +43,7 @@ void ItemList::_shape(int p_idx) {
} else {
item.text_buf->set_direction((TextServer::Direction)item.text_direction);
}
- item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (item.language != "") ? item.language : TranslationServer::get_singleton()->get_tool_locale());
+ item.text_buf->add_string(item.text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), item.opentype_features, (!item.language.is_empty()) ? item.language : TranslationServer::get_singleton()->get_tool_locale());
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
item.text_buf->set_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND);
} else {
@@ -653,7 +653,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
if (p_event->is_pressed() && items.size() > 0) {
if (p_event->is_action("ui_up")) {
- if (search_string != "") {
+ if (!search_string.is_empty()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
@@ -683,7 +683,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) {
accept_event();
}
} else if (p_event->is_action("ui_down")) {
- if (search_string != "") {
+ if (!search_string.is_empty()) {
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - search_time_msec;
@@ -920,7 +920,7 @@ void ItemList::_notification(int p_what) {
minsize = items[i].get_icon_size() * icon_scale;
}
- if (items[i].text != "") {
+ if (!items[i].text.is_empty()) {
if (icon_mode == ICON_MODE_TOP) {
minsize.y += icon_margin;
} else {
@@ -929,7 +929,7 @@ void ItemList::_notification(int p_what) {
}
}
- if (items[i].text != "") {
+ if (!items[i].text.is_empty()) {
int max_width = -1;
if (fixed_column_width) {
max_width = fixed_column_width;
@@ -1037,7 +1037,7 @@ void ItemList::_notification(int p_what) {
}
}
- minimum_size_changed();
+ update_minimum_size();
shape_changed = false;
}
@@ -1188,7 +1188,7 @@ void ItemList::_notification(int p_what) {
draw_texture(items[i].tag_icon, draw_pos + base_ofs);
}
- if (items[i].text != "") {
+ if (!items[i].text.is_empty()) {
int max_len = -1;
Vector2 size2 = items[i].text_buf->get_size();
@@ -1213,7 +1213,7 @@ void ItemList::_notification(int p_what) {
text_ofs.x = size.width - text_ofs.x - max_len;
}
- items.write[i].text_buf->set_align(HALIGN_CENTER);
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER);
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);
@@ -1241,9 +1241,9 @@ void ItemList::_notification(int p_what) {
items.write[i].text_buf->set_width(max_len);
if (rtl) {
- items.write[i].text_buf->set_align(HALIGN_RIGHT);
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
} else {
- items.write[i].text_buf->set_align(HALIGN_LEFT);
+ items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT);
}
if (outline_size > 0 && font_outline_color.a > 0) {
@@ -1360,10 +1360,10 @@ String ItemList::get_tooltip(const Point2 &p_pos) const {
if (!items[closest].tooltip_enabled) {
return "";
}
- if (items[closest].tooltip != "") {
+ if (!items[closest].tooltip.is_empty()) {
return items[closest].tooltip;
}
- if (items[closest].text != "") {
+ if (!items[closest].text.is_empty()) {
return items[closest].text;
}
}
@@ -1492,6 +1492,9 @@ bool ItemList::_set(const StringName &p_name, const Variant &p_value) {
} else if (components[1] == "disabled") {
set_item_disabled(item_index, p_value);
return true;
+ } else if (components[1] == "selectable") {
+ set_item_selectable(item_index, p_value);
+ return true;
}
}
#ifndef DISABLE_DEPRECATED
@@ -1528,6 +1531,9 @@ bool ItemList::_get(const StringName &p_name, Variant &r_ret) const {
} else if (components[1] == "disabled") {
r_ret = is_item_disabled(item_index);
return true;
+ } else if (components[1] == "selectable") {
+ r_ret = is_item_selectable(item_index);
+ return true;
}
}
return false;
@@ -1541,6 +1547,10 @@ void ItemList::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
+ pi = PropertyInfo(Variant::BOOL, vformat("item_%d/selectable", i));
+ pi.usage &= ~(is_item_selectable(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
pi.usage &= ~(!is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
@@ -1652,7 +1662,7 @@ void ItemList::_bind_methods() {
ClassDB::bind_method(D_METHOD("ensure_current_is_visible"), &ItemList::ensure_current_is_visible);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &ItemList::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ItemList::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &ItemList::set_text_overrun_behavior);
ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &ItemList::get_text_overrun_behavior);
@@ -1663,7 +1673,7 @@ void ItemList::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_text_lines", PROPERTY_HINT_RANGE, "1,10,1,or_greater"), "set_max_text_lines", "get_max_text_lines");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_height"), "set_auto_height", "has_auto_height");
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_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
ADD_GROUP("Columns", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_columns", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), "set_max_columns", "get_max_columns");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "same_column_width"), "set_same_column_width", "is_same_column_width");
diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h
index e780179e7b..77e910870f 100644
--- a/scene/gui/item_list.h
+++ b/scene/gui/item_list.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -62,7 +62,7 @@ private:
String language;
TextDirection text_direction = TEXT_DIRECTION_AUTO;
- bool selectable = false;
+ bool selectable = true;
bool selected = false;
bool disabled = false;
bool tooltip_enabled = true;
@@ -255,7 +255,7 @@ public:
void set_autoscroll_to_bottom(const bool p_enable);
- VScrollBar *get_v_scroll() { return scroll_bar; }
+ VScrollBar *get_v_scroll_bar() { return scroll_bar; }
ItemList();
~ItemList();
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index 50908f6a77..fab420d593 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -44,7 +44,7 @@ void Label::set_autowrap_mode(Label::AutowrapMode p_mode) {
update();
if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -83,6 +83,7 @@ void Label::_shape() {
int width = (get_size().width - style->get_minimum_size().width);
if (dirty) {
+ String lang = (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale();
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);
@@ -92,11 +93,11 @@ void Label::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- String text = (uppercase) ? xl_text.to_upper() : xl_text;
- if (visible_chars >= 0) {
+ String text = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
+ 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, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_add_string(text_rid, text, font->get_rids(), font_size, opentype_features, lang);
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, text));
dirty = false;
lines_dirty = true;
@@ -175,7 +176,7 @@ void Label::_shape() {
if (lines_hidden) {
overrun_flags |= TextServer::OVERRUN_ENFORCE_ELLIPSIS;
}
- if (align == ALIGN_FILL) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
for (int i = 0; i < lines_rid.size(); i++) {
if (i < visible_lines - 1 || lines_rid.size() == 1) {
TS->shaped_text_fit_to_width(lines_rid[i], width);
@@ -183,15 +184,13 @@ void Label::_shape() {
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
}
}
-
} else if (lines_hidden) {
TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
}
-
} else {
// Autowrap disabled.
for (int i = 0; i < lines_rid.size(); i++) {
- if (align == ALIGN_FILL) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
TS->shaped_text_fit_to_width(lines_rid[i], width);
overrun_flags |= TextServer::OVERRUN_JUSTIFICATION_AWARE;
TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
@@ -207,7 +206,7 @@ void Label::_shape() {
_update_visible();
if (autowrap_mode == AUTOWRAP_OFF || !clip || overrun_behavior == OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -294,7 +293,7 @@ void Label::_notification(int p_what) {
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_direction(text_rid);
+ 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()));
@@ -316,31 +315,38 @@ void Label::_notification(int p_what) {
}
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 (valign) {
- case VALIGN_TOP: {
+ switch (vertical_alignment) {
+ case VERTICAL_ALIGNMENT_TOP: {
// Nothing.
} break;
- case VALIGN_CENTER: {
+ case VERTICAL_ALIGNMENT_CENTER: {
vbegin = (size.y - (total_h - line_spacing)) / 2;
vsep = 0;
} break;
- case VALIGN_BOTTOM: {
+ case VERTICAL_ALIGNMENT_BOTTOM: {
vbegin = size.y - (total_h - line_spacing);
vsep = 0;
} break;
- case VALIGN_FILL: {
+ case VERTICAL_ALIGNMENT_FILL: {
vbegin = 0;
if (lines_visible > 1) {
vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1);
@@ -358,25 +364,25 @@ void Label::_notification(int p_what) {
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 (align) {
- case ALIGN_FILL:
+ 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 ALIGN_LEFT: {
+ 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 ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
ofs.x = int(size.width - line_size.width) / 2;
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl_layout) {
ofs.x = style->get_offset().x;
} else {
@@ -395,14 +401,19 @@ void Label::_notification(int p_what) {
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.
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ 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;
}
}
@@ -410,22 +421,26 @@ void Label::_notification(int p_what) {
// Draw main text.
for (int j = 0; j < gl_size; j++) {
- for (int k = 0; k < glyphs[j].repeat; k++) {
- // Trim when necessary.
- if (trim_pos >= 0) {
- if (rtl) {
- if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- continue;
- }
- } else {
- if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- break;
- }
+ // 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));
// Draw glyph outlines and shadow.
- draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ 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;
}
}
@@ -433,8 +448,12 @@ void Label::_notification(int p_what) {
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.
- draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs);
+ 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;
}
}
@@ -447,8 +466,12 @@ void Label::_notification(int p_what) {
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.
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ if (!skip) {
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ }
+ processed_glyphs++;
ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
@@ -456,22 +479,26 @@ void Label::_notification(int p_what) {
// Draw main text.
for (int j = 0; j < gl_size; j++) {
- for (int k = 0; k < glyphs[j].repeat; k++) {
- // Trim when necessary.
- if (trim_pos >= 0) {
- if (rtl) {
- if (j < trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- continue;
- }
- } else {
- if (j >= trim_pos && (glyphs[j].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- break;
- }
+ // 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.
- draw_glyph(glyphs[j], ci, font_color, ofs);
+ if (!skip) {
+ draw_glyph(glyphs[j], ci, font_color, ofs);
+ }
+ processed_glyphs++;
ofs.x += glyphs[j].advance;
}
}
@@ -479,8 +506,12 @@ void Label::_notification(int p_what) {
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.
- draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ if (!skip) {
+ draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs);
+ }
+ processed_glyphs++;
ofs.x += ellipsis_glyphs[gl_idx].advance;
}
}
@@ -556,29 +587,29 @@ int Label::get_visible_line_count() const {
return lines_visible;
}
-void Label::set_align(Align p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- if (align != p_align) {
- if (align == ALIGN_FILL || p_align == ALIGN_FILL) {
+void Label::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (horizontal_alignment != p_alignment) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL || p_alignment == HORIZONTAL_ALIGNMENT_FILL) {
lines_dirty = true; // Reshape lines.
}
- align = p_align;
+ horizontal_alignment = p_alignment;
}
update();
}
-Label::Align Label::get_align() const {
- return align;
+HorizontalAlignment Label::get_horizontal_alignment() const {
+ return horizontal_alignment;
}
-void Label::set_valign(VAlign p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- valign = p_align;
+void Label::set_vertical_alignment(VerticalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ vertical_alignment = p_alignment;
update();
}
-Label::VAlign Label::get_valign() const {
- return valign;
+VerticalAlignment Label::get_vertical_alignment() const {
+ return vertical_alignment;
}
void Label::set_text(const String &p_string) {
@@ -592,7 +623,7 @@ void Label::set_text(const String &p_string) {
visible_chars = get_total_character_count() * percent_visible;
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
void Label::set_text_direction(Control::TextDirection p_text_direction) {
@@ -668,7 +699,7 @@ String Label::get_language() const {
void Label::set_clip_text(bool p_clip) {
clip = p_clip;
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool Label::is_clipping_text() const {
@@ -682,7 +713,7 @@ void Label::set_text_overrun_behavior(Label::OverrunBehavior p_behavior) {
}
update();
if (clip || overrun_behavior != OVERRUN_NO_TRIMMING) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -702,7 +733,9 @@ void Label::set_visible_characters(int p_amount) {
} else {
percent_visible = 1.0;
}
- dirty = true;
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ dirty = true;
+ }
update();
}
}
@@ -720,7 +753,9 @@ void Label::set_percent_visible(float p_percent) {
visible_chars = get_total_character_count() * p_percent;
percent_visible = p_percent;
}
- dirty = true;
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ dirty = true;
+ }
update();
}
}
@@ -729,6 +764,18 @@ float Label::get_percent_visible() const {
return percent_visible;
}
+Label::VisibleCharactersBehavior Label::get_visible_characters_behavior() const {
+ return visible_chars_behavior;
+}
+
+void Label::set_visible_characters_behavior(Label::VisibleCharactersBehavior p_behavior) {
+ if (visible_chars_behavior != p_behavior) {
+ visible_chars_behavior = p_behavior;
+ dirty = true;
+ update();
+ }
+}
+
void Label::set_lines_skipped(int p_lines) {
ERR_FAIL_COND(p_lines < 0);
lines_skipped = p_lines;
@@ -763,7 +810,7 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -771,7 +818,7 @@ bool Label::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
dirty = true;
update();
@@ -803,16 +850,16 @@ bool Label::_get(const StringName &p_name, Variant &r_ret) const {
void Label::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
void Label::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_align", "align"), &Label::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &Label::get_align);
- ClassDB::bind_method(D_METHOD("set_valign", "valign"), &Label::set_valign);
- ClassDB::bind_method(D_METHOD("get_valign"), &Label::get_valign);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &Label::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &Label::get_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("set_vertical_alignment", "alignment"), &Label::set_vertical_alignment);
+ ClassDB::bind_method(D_METHOD("get_vertical_alignment"), &Label::get_vertical_alignment);
ClassDB::bind_method(D_METHOD("set_text", "text"), &Label::set_text);
ClassDB::bind_method(D_METHOD("get_text"), &Label::get_text);
ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Label::set_text_direction);
@@ -836,6 +883,8 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_total_character_count"), &Label::get_total_character_count);
ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &Label::set_visible_characters);
ClassDB::bind_method(D_METHOD("get_visible_characters"), &Label::get_visible_characters);
+ ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &Label::get_visible_characters_behavior);
+ ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &Label::set_visible_characters_behavior);
ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &Label::set_percent_visible);
ClassDB::bind_method(D_METHOD("get_percent_visible"), &Label::get_percent_visible);
ClassDB::bind_method(D_METHOD("set_lines_skipped", "lines_skipped"), &Label::set_lines_skipped);
@@ -847,16 +896,6 @@ void Label::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_structured_text_bidi_override_options", "args"), &Label::set_structured_text_bidi_override_options);
ClassDB::bind_method(D_METHOD("get_structured_text_bidi_override_options"), &Label::get_structured_text_bidi_override_options);
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
-
- BIND_ENUM_CONSTANT(VALIGN_TOP);
- BIND_ENUM_CONSTANT(VALIGN_CENTER);
- BIND_ENUM_CONSTANT(VALIGN_BOTTOM);
- BIND_ENUM_CONSTANT(VALIGN_FILL);
-
BIND_ENUM_CONSTANT(AUTOWRAP_OFF);
BIND_ENUM_CONSTANT(AUTOWRAP_ARBITRARY);
BIND_ENUM_CONSTANT(AUTOWRAP_WORD);
@@ -868,16 +907,24 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(OVERRUN_TRIM_ELLIPSIS);
BIND_ENUM_CONSTANT(OVERRUN_TRIM_WORD_ELLIPSIS);
+ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
+ BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
+ 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"), "set_language", "get_language");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
+ 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, "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");
diff --git a/scene/gui/label.h b/scene/gui/label.h
index 8b48eb9670..354e9c664d 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -37,20 +37,6 @@ class Label : public Control {
GDCLASS(Label, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
- };
-
- enum VAlign {
- VALIGN_TOP,
- VALIGN_CENTER,
- VALIGN_BOTTOM,
- VALIGN_FILL
- };
-
enum AutowrapMode {
AUTOWRAP_OFF,
AUTOWRAP_ARBITRARY,
@@ -66,9 +52,17 @@ public:
OVERRUN_TRIM_WORD_ELLIPSIS,
};
+ enum VisibleCharactersBehavior {
+ VC_CHARS_BEFORE_SHAPING,
+ VC_CHARS_AFTER_SHAPING,
+ VC_GLYPHS_AUTO,
+ VC_GLYPHS_LTR,
+ VC_GLYPHS_RTL,
+ };
+
private:
- Align align = ALIGN_LEFT;
- VAlign valign = VALIGN_TOP;
+ HorizontalAlignment horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ VerticalAlignment vertical_alignment = VERTICAL_ALIGNMENT_TOP;
String text;
String xl_text;
AutowrapMode autowrap_mode = AUTOWRAP_OFF;
@@ -90,6 +84,7 @@ private:
float percent_visible = 1.0;
+ VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
int visible_chars = -1;
int lines_skipped = 0;
int max_lines_visible = -1;
@@ -109,11 +104,11 @@ protected:
public:
virtual Size2 get_minimum_size() const override;
- void set_align(Align p_align);
- Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
- void set_valign(VAlign p_align);
- VAlign get_valign() const;
+ void set_vertical_alignment(VerticalAlignment p_alignment);
+ VerticalAlignment get_vertical_alignment() const;
void set_text(const String &p_string);
String get_text() const;
@@ -140,6 +135,9 @@ public:
void set_uppercase(bool p_uppercase);
bool is_uppercase() const;
+ VisibleCharactersBehavior get_visible_characters_behavior() const;
+ void set_visible_characters_behavior(VisibleCharactersBehavior p_behavior);
+
void set_visible_characters(int p_amount);
int get_visible_characters() const;
int get_total_character_count() const;
@@ -167,9 +165,8 @@ public:
~Label();
};
-VARIANT_ENUM_CAST(Label::Align);
-VARIANT_ENUM_CAST(Label::VAlign);
VARIANT_ENUM_CAST(Label::AutowrapMode);
VARIANT_ENUM_CAST(Label::OverrunBehavior);
+VARIANT_ENUM_CAST(Label::VisibleCharactersBehavior);
#endif
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 69b08fda3c..88953fa7db 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -227,7 +227,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
_ensure_menu();
- menu->set_position(get_screen_transform().xform(get_local_mouse_position()));
+ menu->set_position(get_screen_position() + get_local_mouse_position());
menu->reset_size();
menu->popup();
grab_focus();
@@ -322,7 +322,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
deselect();
selection.start_column = caret_column;
selection.creating = true;
- } else if (selection.enabled) {
+ } else if (selection.enabled && !selection.double_click) {
selection.drag_attempt = true;
}
}
@@ -392,7 +392,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
if (k->is_action("ui_menu", true)) {
_ensure_menu();
Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + get_theme_font(SNAME("font"))->get_height(get_theme_font_size(SNAME("font_size")))) / 2);
- menu->set_position(get_global_transform().xform(pos));
+ menu->set_position(get_screen_position() + pos);
menu->reset_size();
menu->popup();
menu->grab_focus();
@@ -548,17 +548,17 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
-void LineEdit::set_align(Align p_align) {
- ERR_FAIL_INDEX((int)p_align, 4);
- if (align != p_align) {
- align = p_align;
+void LineEdit::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ ERR_FAIL_INDEX((int)p_alignment, 4);
+ if (alignment != p_alignment) {
+ alignment = p_alignment;
_shape();
}
update();
}
-LineEdit::Align LineEdit::get_align() const {
- return align;
+HorizontalAlignment LineEdit::get_horizontal_alignment() const {
+ return alignment;
}
Variant LineEdit::get_drag_data(const Point2 &p_point) {
@@ -588,13 +588,19 @@ void LineEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
if (p_data.get_type() == Variant::STRING && is_editable()) {
set_caret_at_pixel_pos(p_point.x);
int caret_column_tmp = caret_column;
+ bool is_inside_sel = selection.enabled && caret_column >= selection.begin && caret_column <= selection.end;
+ if (Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ is_inside_sel = selection.enabled && caret_column > selection.begin && caret_column < selection.end;
+ }
if (selection.drag_attempt) {
selection.drag_attempt = false;
- if (caret_column < selection.begin || caret_column > selection.end) {
- if (caret_column_tmp > selection.end) {
- caret_column_tmp = caret_column_tmp - (selection.end - selection.begin);
+ if (!is_inside_sel) {
+ if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (caret_column_tmp > selection.end) {
+ caret_column_tmp = caret_column_tmp - (selection.end - selection.begin);
+ }
+ selection_delete();
}
- selection_delete();
set_caret_column(caret_column_tmp);
insert_text_at_caret(p_data);
@@ -709,23 +715,23 @@ void LineEdit::_notification(int p_what) {
float text_width = TS->shaped_text_get_size(text_rid).x;
float text_height = TS->shaped_text_get_size(text_rid).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM);
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -763,7 +769,7 @@ void LineEdit::_notification(int p_what) {
r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon);
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -774,8 +780,6 @@ void LineEdit::_notification(int p_what) {
ofs_max -= r_icon->get_width();
}
- int caret_width = Math::round(1 * get_theme_default_base_scale());
-
// Draw selections rects.
Vector2 ofs = Point2(x_ofs + scroll_offset, y_ofs);
if (selection.enabled) {
@@ -837,6 +841,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();
+
if (ime_text.length() == 0) {
// Normal caret.
CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column);
@@ -975,7 +981,7 @@ void LineEdit::_notification(int p_what) {
if (is_drag_successful()) {
if (selection.drag_attempt) {
selection.drag_attempt = false;
- if (is_editable()) {
+ if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
selection_delete();
} else if (deselect_on_focus_loss_enabled) {
deselect();
@@ -1011,7 +1017,7 @@ void LineEdit::paste_text() {
// Strip escape characters like \n and \t as they can't be displayed on LineEdit.
String paste_buffer = DisplayServer::get_singleton()->clipboard_get().strip_escapes();
- if (paste_buffer != "") {
+ if (!paste_buffer.is_empty()) {
int prev_len = text.length();
if (selection.enabled) {
selection_delete();
@@ -1109,23 +1115,23 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1138,7 +1144,7 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1157,23 +1163,23 @@ Vector2i LineEdit::get_caret_pixel_pos() {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1186,7 +1192,7 @@ Vector2i LineEdit::get_caret_pixel_pos() {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1502,23 +1508,23 @@ void LineEdit::set_caret_column(int p_column) {
int x_ofs = 0;
float text_width = TS->shaped_text_get_size(text_rid).x;
- switch (align) {
- case ALIGN_FILL:
- case ALIGN_LEFT: {
+ switch (alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - style->get_margin(SIDE_RIGHT) - (text_width)));
} else {
x_ofs = style->get_offset().x;
}
} break;
- case ALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
if (scroll_offset != 0) {
x_ofs = style->get_offset().x;
} else {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - (text_width)) / 2);
}
} break;
- case ALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (rtl) {
x_ofs = style->get_offset().x;
} else {
@@ -1532,7 +1538,7 @@ void LineEdit::set_caret_column(int p_column) {
bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled;
if (right_icon.is_valid() || display_clear_icon) {
Ref<Texture2D> r_icon = display_clear_icon ? Control::get_theme_icon(SNAME("clear")) : right_icon;
- if (align == ALIGN_CENTER) {
+ if (alignment == HORIZONTAL_ALIGNMENT_CENTER) {
if (scroll_offset == 0) {
x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2);
}
@@ -1712,7 +1718,7 @@ void LineEdit::set_editable(bool p_editable) {
editable = p_editable;
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -1942,7 +1948,7 @@ void LineEdit::_editor_settings_changed() {
void LineEdit::set_expand_to_text_length_enabled(bool p_enabled) {
expand_to_text_length = p_enabled;
- minimum_size_changed();
+ update_minimum_size();
set_caret_column(caret_column);
}
@@ -1956,7 +1962,7 @@ void LineEdit::set_clear_button_enabled(bool p_enabled) {
}
clear_button_enabled = p_enabled;
_fit_to_width();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -2017,7 +2023,7 @@ void LineEdit::set_right_icon(const Ref<Texture2D> &p_icon) {
}
right_icon = p_icon;
_fit_to_width();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -2072,7 +2078,7 @@ void LineEdit::_shape() {
const Ref<Font> &font = get_theme_font(SNAME("font"));
int font_size = get_theme_font_size(SNAME("font_size"));
ERR_FAIL_COND(font.is_null());
- TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
full_width = TS->shaped_text_get_size(text_rid).x;
@@ -2081,12 +2087,12 @@ void LineEdit::_shape() {
Size2 size = TS->shaped_text_get_size(text_rid);
if ((expand_to_text_length && old_size.x != size.x) || (old_size.y != size.y)) {
- minimum_size_changed();
+ update_minimum_size();
}
}
void LineEdit::_fit_to_width() {
- if (align == ALIGN_FILL) {
+ if (alignment == HORIZONTAL_ALIGNMENT_FILL) {
Ref<StyleBox> style = get_theme_stylebox(SNAME("normal"));
int t_width = get_size().width - style->get_margin(SIDE_RIGHT) - style->get_margin(SIDE_LEFT);
bool using_placeholder = text.is_empty() && ime_text.is_empty();
@@ -2158,7 +2164,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -2166,7 +2172,7 @@ bool LineEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -2198,7 +2204,7 @@ bool LineEdit::_get(const StringName &p_name, Variant &r_ret) const {
void LineEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -2212,8 +2218,8 @@ void LineEdit::_validate_property(PropertyInfo &property) const {
void LineEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_text_changed"), &LineEdit::_text_changed);
- ClassDB::bind_method(D_METHOD("set_align", "align"), &LineEdit::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &LineEdit::get_align);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &LineEdit::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &LineEdit::get_horizontal_alignment);
ClassDB::bind_method(D_METHOD("clear"), &LineEdit::clear);
ClassDB::bind_method(D_METHOD("select", "from", "to"), &LineEdit::select, DEFVAL(0), DEFVAL(-1));
@@ -2291,11 +2297,6 @@ void LineEdit::_bind_methods() {
ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring")));
ADD_SIGNAL(MethodInfo("text_submitted", PropertyInfo(Variant::STRING, "new_text")));
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
-
BIND_ENUM_CONSTANT(MENU_CUT);
BIND_ENUM_CONSTANT(MENU_COPY);
BIND_ENUM_CONSTANT(MENU_PASTE);
@@ -2327,7 +2328,7 @@ void LineEdit::_bind_methods() {
BIND_ENUM_CONSTANT(MENU_MAX);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret");
@@ -2343,7 +2344,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
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"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
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");
diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h
index 221dd9eb2e..0c313f71c2 100644
--- a/scene/gui/line_edit.h
+++ b/scene/gui/line_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,13 +38,6 @@ class LineEdit : public Control {
GDCLASS(LineEdit, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
- };
-
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -78,7 +71,7 @@ public:
};
private:
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
bool editable = false;
bool pass = false;
@@ -104,7 +97,7 @@ private:
PopupMenu *menu_dir = nullptr;
PopupMenu *menu_ctl = nullptr;
- bool caret_mid_grapheme_enabled = false;
+ bool caret_mid_grapheme_enabled = true;
int caret_column = 0;
int scroll_offset = 0;
@@ -218,8 +211,8 @@ protected:
void _validate_property(PropertyInfo &property) const override;
public:
- void set_align(Align p_align);
- Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
virtual Variant get_drag_data(const Point2 &p_point) override;
virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
@@ -347,7 +340,6 @@ public:
~LineEdit();
};
-VARIANT_ENUM_CAST(LineEdit::Align);
VARIANT_ENUM_CAST(LineEdit::MenuItems);
#endif
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index c3201186ea..0ff05faf85 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -42,7 +42,7 @@ void LinkButton::_shape() {
text_buf->set_direction((TextServer::Direction)text_direction);
}
TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
- text_buf->add_string(xl_text, font, font_size, opentype_features, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text_buf->add_string(xl_text, font, font_size, opentype_features, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
}
void LinkButton::set_text(const String &p_text) {
@@ -52,7 +52,7 @@ void LinkButton::set_text(const String &p_text) {
text = p_text;
xl_text = atr(text);
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
}
@@ -149,7 +149,7 @@ void LinkButton::_notification(int p_what) {
xl_text = atr(text);
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
@@ -157,7 +157,7 @@ void LinkButton::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED: {
_shape();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_DRAW: {
@@ -240,7 +240,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -248,7 +248,7 @@ bool LinkButton::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
_shape();
update();
@@ -280,7 +280,7 @@ bool LinkButton::_get(const StringName &p_name, Variant &r_ret) const {
void LinkButton::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -308,7 +308,7 @@ void LinkButton::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
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"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
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");
diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h
index 231543c63c..7d302e967d 100644
--- a/scene/gui/link_button.h
+++ b/scene/gui/link_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp
index 50b4d192a9..7b696ddb84 100644
--- a/scene/gui/margin_container.cpp
+++ b/scene/gui/margin_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -90,7 +90,7 @@ void MarginContainer::_notification(int p_what) {
}
} break;
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
}
}
diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h
index b782976ada..3a2f0fa8b3 100644
--- a/scene/gui/margin_container.h
+++ b/scene/gui/margin_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp
index 39c7b04955..f7805136f9 100644
--- a/scene/gui/menu_button.cpp
+++ b/scene/gui/menu_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -184,7 +184,7 @@ void MenuButton::_get_property_list(List<PropertyInfo> *p_list) const {
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, "1,10,1,or_greater");
+ pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
@@ -207,7 +207,7 @@ void MenuButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_count"), &MenuButton::get_item_count);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "switch_on_hover"), "set_switch_on_hover", "is_switch_on_hover");
- ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "popup/item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
ADD_SIGNAL(MethodInfo("about_to_popup"));
}
diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h
index 455b7dc870..3647a69d33 100644
--- a/scene/gui/menu_button.h
+++ b/scene/gui/menu_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp
index 8bf25ac915..779d1307f5 100644
--- a/scene/gui/nine_patch_rect.cpp
+++ b/scene/gui/nine_patch_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -97,7 +97,7 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) {
if (texture.is_valid())
texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites
*/
- minimum_size_changed();
+ update_minimum_size();
emit_signal(SceneStringNames::get_singleton()->texture_changed);
}
@@ -109,7 +109,7 @@ void NinePatchRect::set_patch_margin(Side p_side, int p_size) {
ERR_FAIL_INDEX((int)p_side, 4);
margin[p_side] = p_size;
update();
- minimum_size_changed();
+ update_minimum_size();
}
int NinePatchRect::get_patch_margin(Side p_side) const {
diff --git a/scene/gui/nine_patch_rect.h b/scene/gui/nine_patch_rect.h
index f9a3f31fe5..23aebbb782 100644
--- a/scene/gui/nine_patch_rect.h
+++ b/scene/gui/nine_patch_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index dcf3cfeb09..90bb316448 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -110,6 +110,63 @@ 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") {
+ bool valid;
+ popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid);
+
+ int idx = components[1].get_slice("_", 1).to_int();
+ if (idx == current) {
+ // Force refreshing currently displayed item.
+ current = -1;
+ _select(idx, false);
+ }
+
+ return valid;
+ }
+ return false;
+}
+
+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") {
+ bool valid;
+ r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid);
+ return valid;
+ }
+ return false;
+}
+
+void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < popup->get_item_count(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("popup/item_%d/text", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("popup/item_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ 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, "1,10,1,or_greater");
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/disabled", i));
+ pi.usage &= ~(!popup->is_item_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/separator", i));
+ pi.usage &= ~(!popup->is_item_separator(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
void OptionButton::_focused(int p_which) {
emit_signal(SNAME("item_focused"), p_which);
}
@@ -122,6 +179,7 @@ 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);
popup->popup();
}
@@ -191,6 +249,12 @@ bool OptionButton::is_item_disabled(int p_idx) const {
return popup->is_item_disabled(p_idx);
}
+void OptionButton::set_item_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ popup->set_item_count(p_count);
+ notify_property_list_changed();
+}
+
int OptionButton::get_item_count() const {
return popup->get_item_count();
}
@@ -267,38 +331,6 @@ PopupMenu *OptionButton::get_popup() const {
return popup;
}
-Array OptionButton::_get_items() const {
- Array items;
- for (int i = 0; i < get_item_count(); i++) {
- items.push_back(get_item_text(i));
- items.push_back(get_item_icon(i));
- items.push_back(is_item_disabled(i));
- items.push_back(get_item_id(i));
- items.push_back(get_item_metadata(i));
- }
-
- return items;
-}
-
-void OptionButton::_set_items(const Array &p_items) {
- ERR_FAIL_COND(p_items.size() % 5);
- clear();
-
- for (int i = 0; i < p_items.size(); i += 5) {
- String text = p_items[i + 0];
- Ref<Texture2D> icon = p_items[i + 1];
- bool disabled = p_items[i + 2];
- int id = p_items[i + 3];
- Variant meta = p_items[i + 4];
-
- int idx = get_item_count();
- add_item(text, id);
- set_item_icon(idx, icon);
- set_item_disabled(idx, disabled);
- set_item_metadata(idx, meta);
- }
-}
-
void OptionButton::get_translatable_strings(List<String> *p_strings) const {
popup->get_translatable_strings(p_strings);
}
@@ -317,7 +349,6 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &OptionButton::get_item_index);
ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &OptionButton::get_item_metadata);
ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &OptionButton::is_item_disabled);
- ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count);
ClassDB::bind_method(D_METHOD("add_separator"), &OptionButton::add_separator);
ClassDB::bind_method(D_METHOD("clear"), &OptionButton::clear);
ClassDB::bind_method(D_METHOD("select", "idx"), &OptionButton::select);
@@ -329,11 +360,11 @@ void OptionButton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_popup"), &OptionButton::get_popup);
- ClassDB::bind_method(D_METHOD("_set_items"), &OptionButton::_set_items);
- ClassDB::bind_method(D_METHOD("_get_items"), &OptionButton::_get_items);
+ ClassDB::bind_method(D_METHOD("set_item_count", "count"), &OptionButton::set_item_count);
+ ClassDB::bind_method(D_METHOD("get_item_count"), &OptionButton::get_item_count);
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "items", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_items", "_get_items");
- // "selected" property must come after "items", otherwise GH-10213 occurs.
+ // "selected" property must come after "item_count", otherwise GH-10213 occurs.
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "popup/item_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "selected"), "_select_int", "get_selected");
ADD_SIGNAL(MethodInfo("item_selected", PropertyInfo(Variant::INT, "index")));
ADD_SIGNAL(MethodInfo("item_focused", PropertyInfo(Variant::INT, "index")));
@@ -341,7 +372,7 @@ void OptionButton::_bind_methods() {
OptionButton::OptionButton() {
set_toggle_mode(true);
- set_text_align(ALIGN_LEFT);
+ set_text_alignment(HORIZONTAL_ALIGNMENT_LEFT);
if (is_layout_rtl()) {
if (has_theme_icon(SNAME("arrow"))) {
_set_internal_margin(SIDE_LEFT, Control::get_theme_icon(SNAME("arrow"))->get_width());
diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h
index 953337ecce..adf2bb90ef 100644
--- a/scene/gui/option_button.h
+++ b/scene/gui/option_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -45,14 +45,14 @@ class OptionButton : public Button {
void _select(int p_which, bool p_emit = false);
void _select_int(int p_which);
- Array _get_items() const;
- void _set_items(const Array &p_items);
-
virtual void pressed() override;
protected:
Size2 get_minimum_size() const override;
void _notification(int p_what);
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
static void _bind_methods();
public:
@@ -76,6 +76,7 @@ public:
Variant get_item_metadata(int p_idx) const;
bool is_item_disabled(int p_idx) const;
+ void set_item_count(int p_count);
int get_item_count() const;
void add_separator();
diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp
index e8e7e3d997..86858fdc78 100644
--- a/scene/gui/panel.cpp
+++ b/scene/gui/panel.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/panel.h b/scene/gui/panel.h
index 84fd6aaead..37f14c250c 100644
--- a/scene/gui/panel.h
+++ b/scene/gui/panel.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp
index d910e1e882..463ad3c513 100644
--- a/scene/gui/panel_container.cpp
+++ b/scene/gui/panel_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h
index f27ca7fad7..a5ff74cebb 100644
--- a/scene/gui/panel_container.h
+++ b/scene/gui/panel_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index a48ad0f770..7c03fcbb37 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/popup.h b/scene/gui/popup.h
index 8458a75eef..5678043b23 100644
--- a/scene/gui/popup.h
+++ b/scene/gui/popup.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index 2e854abb76..d7139d0140 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,7 +50,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
int hseparation = get_theme_constant(SNAME("hseparation"));
Size2 minsize = get_theme_stylebox(SNAME("panel"))->get_minimum_size(); // Accounts for margin in the margin container
- minsize.x += scroll_container->get_v_scrollbar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
+ minsize.x += scroll_container->get_v_scroll_bar()->get_size().width * 2; // Adds a buffer so that the scrollbar does not render over the top of content
float max_w = 0.0;
float icon_w = 0.0;
@@ -80,7 +80,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
accel_max_w = MAX(accel_w, accel_max_w);
}
- if (items[i].submenu != "") {
+ if (!items[i].submenu.is_empty()) {
size.width += get_theme_icon(SNAME("submenu"))->get_width();
}
@@ -216,7 +216,7 @@ void PopupMenu::_activate_submenu(int p_over) {
submenu_pos.x = this_pos.x + submenu_size.width;
}
- if (submenu_pos.x + submenu_size.width > get_parent_rect().size.width) {
+ if (submenu_pos.x + submenu_size.width > get_parent_rect().position.x + get_parent_rect().size.width) {
submenu_pos.x = this_pos.x - submenu_size.width;
}
@@ -326,13 +326,13 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
set_input_as_handled();
}
} else if (p_event->is_action("ui_right") && p_event->is_pressed()) {
- if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator && items[mouse_over].submenu != "" && submenu_over != mouse_over) {
+ if (mouse_over >= 0 && mouse_over < items.size() && !!items[mouse_over].separator && items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
_activate_submenu(mouse_over);
set_input_as_handled();
}
} else if (p_event->is_action("ui_accept") && p_event->is_pressed()) {
if (mouse_over >= 0 && mouse_over < items.size() && !items[mouse_over].separator) {
- if (items[mouse_over].submenu != "" && submenu_over != mouse_over) {
+ if (!items[mouse_over].submenu.is_empty() && submenu_over != mouse_over) {
_activate_submenu(mouse_over);
} else {
activate_item(mouse_over);
@@ -343,11 +343,11 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
Rect2 item_clickable_area = scroll_container->get_rect();
- if (scroll_container->get_v_scrollbar()->is_visible_in_tree()) {
+ if (scroll_container->get_v_scroll_bar()->is_visible_in_tree()) {
if (is_layout_rtl()) {
- item_clickable_area.position.x += scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.position.x += scroll_container->get_v_scroll_bar()->get_size().width;
} else {
- item_clickable_area.size.width -= scroll_container->get_v_scrollbar()->get_size().width;
+ item_clickable_area.size.width -= scroll_container->get_v_scroll_bar()->get_size().width;
}
}
@@ -371,7 +371,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
// Disable clicks under a time threshold to avoid selection right when opening the popup.
uint64_t now = OS::get_singleton()->get_ticks_msec();
uint64_t diff = now - popup_time_msec;
- if (diff < 100) {
+ if (diff < 150) {
return;
}
@@ -387,7 +387,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (items[over].submenu != "") {
+ if (!items[over].submenu.is_empty()) {
_activate_submenu(over);
return;
}
@@ -419,7 +419,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (items[over].submenu != "" && submenu_over != over) {
+ if (!items[over].submenu.is_empty() && submenu_over != over) {
submenu_over = over;
submenu_timer->start();
}
@@ -508,7 +508,7 @@ void PopupMenu::_draw_items() {
Color font_hover_color = get_theme_color(SNAME("font_hover_color"));
Color font_separator_color = get_theme_color(SNAME("font_separator_color"));
- float scroll_width = scroll_container->get_v_scrollbar()->is_visible_in_tree() ? scroll_container->get_v_scrollbar()->get_size().width : 0;
+ float scroll_width = scroll_container->get_v_scroll_bar()->is_visible_in_tree() ? scroll_container->get_v_scroll_bar()->get_size().width : 0;
float display_width = control->get_size().width - scroll_width;
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
@@ -558,7 +558,7 @@ 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 != String()) {
+ 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;
@@ -599,7 +599,7 @@ void PopupMenu::_draw_items() {
}
// Submenu arrow on right hand side
- if (items[i].submenu != "") {
+ 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);
} else {
@@ -611,7 +611,7 @@ void PopupMenu::_draw_items() {
Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
int outline_size = get_theme_constant(SNAME("outline_size"));
if (items[i].separator) {
- if (text != String()) {
+ 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));
if (outline_size > 0 && font_outline_color.a > 0) {
@@ -701,7 +701,7 @@ void PopupMenu::_shape_item(int p_item) {
} else {
items.write[p_item].text_buf->set_direction((TextServer::Direction)items[p_item].text_direction);
}
- items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, (items[p_item].language != "") ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
+ items.write[p_item].text_buf->add_string(items.write[p_item].xl_text, font, font_size, items[p_item].opentype_features, !items[p_item].language.is_empty() ? items[p_item].language : TranslationServer::get_singleton()->get_tool_locale());
items.write[p_item].accel_text_buf->clear();
items.write[p_item].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
@@ -736,7 +736,7 @@ void PopupMenu::_notification(int p_what) {
grab_focus();
} break;
case NOTIFICATION_WM_MOUSE_EXIT: {
- if (mouse_over >= 0 && (items[mouse_over].submenu == "" || submenu_over != -1)) {
+ if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) {
mouse_over = -1;
control->update();
}
@@ -769,7 +769,7 @@ void PopupMenu::_notification(int p_what) {
}
for (int i = 0; i < items.size(); i++) {
- if (items[i].submenu == "") {
+ if (items[i].submenu.is_empty()) {
continue;
}
@@ -1269,13 +1269,28 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const {
return items[p_idx].shortcut_is_disabled;
}
+void PopupMenu::set_current_index(int p_idx) {
+ ERR_FAIL_INDEX(p_idx, items.size());
+ mouse_over = p_idx;
+ _scroll_to_item(mouse_over);
+ control->update();
+}
+
int PopupMenu::get_current_index() const {
return mouse_over;
}
void PopupMenu::set_item_count(int p_count) {
ERR_FAIL_COND(p_count < 0);
+ int prev_size = items.size();
items.resize(p_count);
+
+ if (prev_size < p_count) {
+ for (int i = prev_size; i < p_count; i++) {
+ items.write[i].id = i;
+ }
+ }
+
control->update();
child_controls_changed();
notify_property_list_changed();
@@ -1323,7 +1338,7 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo
return true;
}
- if (items[i].submenu != "") {
+ if (!items[i].submenu.is_empty()) {
Node *n = get_node(items[i].submenu);
if (!n) {
continue;
@@ -1412,7 +1427,7 @@ void PopupMenu::add_separator(const String &p_text, int p_id) {
Item sep;
sep.separator = true;
sep.id = p_id;
- if (p_text != String()) {
+ if (!p_text.is_empty()) {
sep.text = p_text;
sep.xl_text = atr(p_text);
}
@@ -1510,7 +1525,7 @@ void PopupMenu::set_parent_rect(const Rect2 &p_rect) {
void PopupMenu::get_translatable_strings(List<String> *p_strings) const {
for (int i = 0; i < items.size(); i++) {
- if (items[i].xl_text != "") {
+ if (!items[i].xl_text.is_empty()) {
p_strings->push_back(items[i].xl_text);
}
}
@@ -1658,7 +1673,7 @@ void PopupMenu::_get_property_list(List<PropertyInfo> *p_list) const {
pi.usage &= ~(!is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0);
p_list->push_back(pi);
- pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "1,10,1,or_greater");
+ pi = PropertyInfo(Variant::INT, vformat("item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater");
p_list->push_back(pi);
pi = PropertyInfo(Variant::BOOL, vformat("item_%d/disabled", i));
@@ -1690,53 +1705,53 @@ void PopupMenu::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_submenu_item", "label", "submenu", "id"), &PopupMenu::add_submenu_item, DEFVAL(-1));
- ClassDB::bind_method(D_METHOD("set_item_text", "idx", "text"), &PopupMenu::set_item_text);
- ClassDB::bind_method(D_METHOD("set_item_text_direction", "idx", "direction"), &PopupMenu::set_item_text_direction);
- ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "idx", "tag", "value"), &PopupMenu::set_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("set_item_language", "idx", "language"), &PopupMenu::set_item_language);
- ClassDB::bind_method(D_METHOD("set_item_icon", "idx", "icon"), &PopupMenu::set_item_icon);
- ClassDB::bind_method(D_METHOD("set_item_checked", "idx", "checked"), &PopupMenu::set_item_checked);
- ClassDB::bind_method(D_METHOD("set_item_id", "idx", "id"), &PopupMenu::set_item_id);
- ClassDB::bind_method(D_METHOD("set_item_accelerator", "idx", "accel"), &PopupMenu::set_item_accelerator);
- ClassDB::bind_method(D_METHOD("set_item_metadata", "idx", "metadata"), &PopupMenu::set_item_metadata);
- ClassDB::bind_method(D_METHOD("set_item_disabled", "idx", "disabled"), &PopupMenu::set_item_disabled);
- ClassDB::bind_method(D_METHOD("set_item_submenu", "idx", "submenu"), &PopupMenu::set_item_submenu);
- ClassDB::bind_method(D_METHOD("set_item_as_separator", "idx", "enable"), &PopupMenu::set_item_as_separator);
- ClassDB::bind_method(D_METHOD("set_item_as_checkable", "idx", "enable"), &PopupMenu::set_item_as_checkable);
- ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "idx", "enable"), &PopupMenu::set_item_as_radio_checkable);
- ClassDB::bind_method(D_METHOD("set_item_tooltip", "idx", "tooltip"), &PopupMenu::set_item_tooltip);
- ClassDB::bind_method(D_METHOD("set_item_shortcut", "idx", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
- ClassDB::bind_method(D_METHOD("set_item_multistate", "idx", "state"), &PopupMenu::set_item_multistate);
- ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "idx", "disabled"), &PopupMenu::set_item_shortcut_disabled);
-
- ClassDB::bind_method(D_METHOD("toggle_item_checked", "idx"), &PopupMenu::toggle_item_checked);
- ClassDB::bind_method(D_METHOD("toggle_item_multistate", "idx"), &PopupMenu::toggle_item_multistate);
-
- ClassDB::bind_method(D_METHOD("get_item_text", "idx"), &PopupMenu::get_item_text);
- ClassDB::bind_method(D_METHOD("get_item_text_direction", "idx"), &PopupMenu::get_item_text_direction);
- ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "idx", "tag"), &PopupMenu::get_item_opentype_feature);
- ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "idx"), &PopupMenu::clear_item_opentype_features);
- ClassDB::bind_method(D_METHOD("get_item_language", "idx"), &PopupMenu::get_item_language);
- ClassDB::bind_method(D_METHOD("get_item_icon", "idx"), &PopupMenu::get_item_icon);
- ClassDB::bind_method(D_METHOD("is_item_checked", "idx"), &PopupMenu::is_item_checked);
- ClassDB::bind_method(D_METHOD("get_item_id", "idx"), &PopupMenu::get_item_id);
+ ClassDB::bind_method(D_METHOD("set_item_text", "index", "text"), &PopupMenu::set_item_text);
+ ClassDB::bind_method(D_METHOD("set_item_text_direction", "index", "direction"), &PopupMenu::set_item_text_direction);
+ ClassDB::bind_method(D_METHOD("set_item_opentype_feature", "index", "tag", "value"), &PopupMenu::set_item_opentype_feature);
+ ClassDB::bind_method(D_METHOD("set_item_language", "index", "language"), &PopupMenu::set_item_language);
+ ClassDB::bind_method(D_METHOD("set_item_icon", "index", "icon"), &PopupMenu::set_item_icon);
+ ClassDB::bind_method(D_METHOD("set_item_checked", "index", "checked"), &PopupMenu::set_item_checked);
+ ClassDB::bind_method(D_METHOD("set_item_id", "index", "id"), &PopupMenu::set_item_id);
+ ClassDB::bind_method(D_METHOD("set_item_accelerator", "index", "accel"), &PopupMenu::set_item_accelerator);
+ ClassDB::bind_method(D_METHOD("set_item_metadata", "index", "metadata"), &PopupMenu::set_item_metadata);
+ ClassDB::bind_method(D_METHOD("set_item_disabled", "index", "disabled"), &PopupMenu::set_item_disabled);
+ ClassDB::bind_method(D_METHOD("set_item_submenu", "index", "submenu"), &PopupMenu::set_item_submenu);
+ ClassDB::bind_method(D_METHOD("set_item_as_separator", "index", "enable"), &PopupMenu::set_item_as_separator);
+ ClassDB::bind_method(D_METHOD("set_item_as_checkable", "index", "enable"), &PopupMenu::set_item_as_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_as_radio_checkable", "index", "enable"), &PopupMenu::set_item_as_radio_checkable);
+ ClassDB::bind_method(D_METHOD("set_item_tooltip", "index", "tooltip"), &PopupMenu::set_item_tooltip);
+ ClassDB::bind_method(D_METHOD("set_item_shortcut", "index", "shortcut", "global"), &PopupMenu::set_item_shortcut, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("set_item_multistate", "index", "state"), &PopupMenu::set_item_multistate);
+ ClassDB::bind_method(D_METHOD("set_item_shortcut_disabled", "index", "disabled"), &PopupMenu::set_item_shortcut_disabled);
+
+ ClassDB::bind_method(D_METHOD("toggle_item_checked", "index"), &PopupMenu::toggle_item_checked);
+ ClassDB::bind_method(D_METHOD("toggle_item_multistate", "index"), &PopupMenu::toggle_item_multistate);
+
+ ClassDB::bind_method(D_METHOD("get_item_text", "index"), &PopupMenu::get_item_text);
+ ClassDB::bind_method(D_METHOD("get_item_text_direction", "index"), &PopupMenu::get_item_text_direction);
+ ClassDB::bind_method(D_METHOD("get_item_opentype_feature", "index", "tag"), &PopupMenu::get_item_opentype_feature);
+ ClassDB::bind_method(D_METHOD("clear_item_opentype_features", "index"), &PopupMenu::clear_item_opentype_features);
+ ClassDB::bind_method(D_METHOD("get_item_language", "index"), &PopupMenu::get_item_language);
+ ClassDB::bind_method(D_METHOD("get_item_icon", "index"), &PopupMenu::get_item_icon);
+ ClassDB::bind_method(D_METHOD("is_item_checked", "index"), &PopupMenu::is_item_checked);
+ ClassDB::bind_method(D_METHOD("get_item_id", "index"), &PopupMenu::get_item_id);
ClassDB::bind_method(D_METHOD("get_item_index", "id"), &PopupMenu::get_item_index);
- ClassDB::bind_method(D_METHOD("get_item_accelerator", "idx"), &PopupMenu::get_item_accelerator);
- ClassDB::bind_method(D_METHOD("get_item_metadata", "idx"), &PopupMenu::get_item_metadata);
- ClassDB::bind_method(D_METHOD("is_item_disabled", "idx"), &PopupMenu::is_item_disabled);
- ClassDB::bind_method(D_METHOD("get_item_submenu", "idx"), &PopupMenu::get_item_submenu);
- ClassDB::bind_method(D_METHOD("is_item_separator", "idx"), &PopupMenu::is_item_separator);
- ClassDB::bind_method(D_METHOD("is_item_checkable", "idx"), &PopupMenu::is_item_checkable);
- ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "idx"), &PopupMenu::is_item_radio_checkable);
- ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "idx"), &PopupMenu::is_item_shortcut_disabled);
- ClassDB::bind_method(D_METHOD("get_item_tooltip", "idx"), &PopupMenu::get_item_tooltip);
- ClassDB::bind_method(D_METHOD("get_item_shortcut", "idx"), &PopupMenu::get_item_shortcut);
+ ClassDB::bind_method(D_METHOD("get_item_accelerator", "index"), &PopupMenu::get_item_accelerator);
+ ClassDB::bind_method(D_METHOD("get_item_metadata", "index"), &PopupMenu::get_item_metadata);
+ ClassDB::bind_method(D_METHOD("is_item_disabled", "index"), &PopupMenu::is_item_disabled);
+ ClassDB::bind_method(D_METHOD("get_item_submenu", "index"), &PopupMenu::get_item_submenu);
+ ClassDB::bind_method(D_METHOD("is_item_separator", "index"), &PopupMenu::is_item_separator);
+ ClassDB::bind_method(D_METHOD("is_item_checkable", "index"), &PopupMenu::is_item_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_radio_checkable", "index"), &PopupMenu::is_item_radio_checkable);
+ ClassDB::bind_method(D_METHOD("is_item_shortcut_disabled", "index"), &PopupMenu::is_item_shortcut_disabled);
+ 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("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("remove_item", "idx"), &PopupMenu::remove_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));
ClassDB::bind_method(D_METHOD("clear"), &PopupMenu::clear);
@@ -1762,7 +1777,7 @@ void PopupMenu::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "submenu_popup_delay"), "set_submenu_popup_delay", "get_submenu_popup_delay");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_search"), "set_allow_search", "get_allow_search");
- ADD_ARRAY_COUNT("Items", "items_count", "set_item_count", "get_item_count", "item_");
+ ADD_ARRAY_COUNT("Items", "item_count", "set_item_count", "get_item_count", "item_");
ADD_SIGNAL(MethodInfo("id_pressed", PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("id_focused", PropertyInfo(Variant::INT, "id")));
diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h
index 22912fb59c..7c2212d82d 100644
--- a/scene/gui/popup_menu.h
+++ b/scene/gui/popup_menu.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -212,6 +212,7 @@ public:
Ref<Shortcut> get_item_shortcut(int p_idx) const;
int get_item_state(int p_idx) const;
+ void set_current_index(int p_idx);
int get_current_index() const;
void set_item_count(int p_count);
diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp
index 2cfaaa2fde..c20fb0d7a8 100644
--- a/scene/gui/progress_bar.cpp
+++ b/scene/gui/progress_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h
index fb6060d932..2d89163f78 100644
--- a/scene/gui/progress_bar.h
+++ b/scene/gui/progress_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp
index c4f05a7975..879f25c8d8 100644
--- a/scene/gui/range.cpp
+++ b/scene/gui/range.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/range.h b/scene/gui/range.h
index 0dc702b19c..c27eeee13c 100644
--- a/scene/gui/range.h
+++ b/scene/gui/range.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp
index 6d7f2cfd57..e2a0d568a1 100644
--- a/scene/gui/reference_rect.cpp
+++ b/scene/gui/reference_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/reference_rect.h b/scene/gui/reference_rect.h
index 7097e83a15..4a2d328162 100644
--- a/scene/gui/reference_rect.h
+++ b/scene/gui/reference_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp
index 076fa132c0..c9516ed6b9 100644
--- a/scene/gui/rich_text_effect.cpp
+++ b/scene/gui/rich_text_effect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/rich_text_effect.h b/scene/gui/rich_text_effect.h
index 5681f9b193..4532a812ee 100644
--- a/scene/gui/rich_text_effect.h
+++ b/scene/gui/rich_text_effect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index fd19fad667..b50fad0e42 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -254,7 +254,12 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
+ // If the column can grow, allow it to grow.
table->columns.write[i].expand = true;
+ } else {
+ // Otherwise make it shrink as much as possible, so that other columns can grow if needs be.
+ // We keep the max width as is to spread the remaining space between the columns later.
+ table->columns.write[i].min_width = 0;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
@@ -264,7 +269,7 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -325,13 +330,16 @@ void RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -383,7 +391,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Add indent.
l.offset.x = _find_margin(l.from, p_base_font, p_base_font_size);
l.text_buf->set_width(p_width - l.offset.x);
- l.text_buf->set_align((HAlign)_find_align(l.from));
+ l.text_buf->set_alignment(_find_alignment(l.from));
l.text_buf->set_direction(_find_direction(l.from));
if (tab_size > 0) { // Align inline tabs.
@@ -397,7 +405,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Item *it_to = (p_line + 1 < p_frame->lines.size()) ? p_frame->lines[p_line + 1].from : nullptr;
int remaining_characters = visible_characters - l.char_offset;
for (Item *it = l.from; it && it != it_to; it = _get_next_item(it)) {
- if (visible_characters >= 0 && remaining_characters <= 0) {
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters <= 0) {
break;
}
switch (it->type) {
@@ -440,7 +448,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
Dictionary font_ftr = _find_font_features(it);
String lang = _find_language(it);
String tx = t->text;
- if (visible_characters >= 0 && remaining_characters >= 0) {
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING && visible_characters >= 0 && remaining_characters >= 0) {
tx = tx.substr(0, remaining_characters);
}
remaining_characters -= tx.length();
@@ -499,7 +507,12 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
for (int i = 0; i < col_count; i++) {
remaining_width -= table->columns[i].min_width;
if (table->columns[i].max_width > table->columns[i].min_width) {
+ // If the column can grow, allow it to grow.
table->columns.write[i].expand = true;
+ } else {
+ // Otherwise make it shrink as much as possible, so that other columns can grow if needs be.
+ // We keep the max width as is to spread the remaining space between the columns later.
+ table->columns.write[i].min_width = 0;
}
if (table->columns[i].expand) {
total_ratio += table->columns[i].expand_ratio;
@@ -509,7 +522,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
// Assign actual widths.
for (int i = 0; i < col_count; i++) {
table->columns.write[i].width = table->columns[i].min_width;
- if (table->columns[i].expand && total_ratio > 0) {
+ if (table->columns[i].expand && total_ratio > 0 && remaining_width > 0) {
table->columns.write[i].width += table->columns[i].expand_ratio * remaining_width / total_ratio;
}
table->total_width += table->columns[i].width + hseparation;
@@ -570,13 +583,16 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
table->columns.write[column].width = MAX(table->columns.write[column].width, ceil(frame->lines[i].text_buf->get_size().x));
if (i > 0) {
- frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y;
+ frame->lines.write[i].offset.y = frame->lines[i - 1].offset.y + frame->lines[i - 1].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
} else {
frame->lines.write[i].offset.y = 0;
}
frame->lines.write[i].offset += offset;
- float h = frame->lines[i].text_buf->get_size().y;
+ float h = frame->lines[i].text_buf->get_size().y + (frame->lines[i].text_buf->get_line_count() - 1) * get_theme_constant(SNAME("line_separation"));
+ if (i > 0) {
+ h += get_theme_constant(SNAME("line_separation"));
+ }
if (frame->min_size_over.y > 0) {
h = MAX(h, frame->min_size_over.y);
}
@@ -621,12 +637,13 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font>
}
}
-int RichTextLabel::_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) {
- Vector2 off;
-
+int RichTextLabel::_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) {
ERR_FAIL_COND_V(p_frame == nullptr, 0);
ERR_FAIL_COND_V(p_line < 0 || p_line >= p_frame->lines.size(), 0);
+ Vector2 off;
+ int line_spacing = get_theme_constant(SNAME("line_separation"));
+
Line &l = p_frame->lines.write[p_line];
Item *it_from = l.from;
@@ -641,6 +658,12 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
bool rtl = (l.text_buf->get_direction() == TextServer::DIRECTION_RTL);
bool lrtl = is_layout_rtl();
+ bool trim_chars = (visible_characters >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING);
+ bool trim_glyphs_ltr = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !lrtl));
+ bool trim_glyphs_rtl = (visible_characters >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && lrtl));
+ int total_glyphs = (trim_glyphs_ltr || trim_glyphs_rtl) ? get_total_glyph_count() : 0;
+ int visible_glyphs = total_glyphs * percent_visible;
+
Vector<int> list_index;
Vector<ItemList *> list_items;
_find_list(l.from, list_index, list_items);
@@ -670,7 +693,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
prefix = segment + prefix;
}
}
- if (prefix != "") {
+ if (!prefix.is_empty()) {
Ref<Font> font = _find_font(l.from);
if (font.is_null()) {
font = get_theme_font(SNAME("normal_font"));
@@ -684,13 +707,13 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
if (!lrtl && p_frame == main) { // Skip Scrollbar.
offx -= scroll_w;
}
- font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HALIGN_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color));
} else {
float offx = 0.0f;
if (lrtl && p_frame == main) { // Skip Scrollbar.
offx += scroll_w;
}
- font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HALIGN_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
+ font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color));
}
}
@@ -706,6 +729,10 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Size2 ctrl_size = get_size();
// Draw text.
for (int line = 0; line < l.text_buf->get_line_count(); line++) {
+ if (line > 0) {
+ off.y += line_spacing;
+ }
+
RID rid = l.text_buf->get_line_rid(line);
if (p_ofs.y + off.y >= ctrl_size.height) {
break;
@@ -734,17 +761,17 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
// Draw text.
- switch (l.text_buf->get_align()) {
- case HALIGN_FILL:
- case HALIGN_LEFT: {
+ switch (l.text_buf->get_alignment()) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
off.x += width - length;
}
} break;
- case HALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
off.x += Math::floor((width - length) / 2.0);
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (!rtl) {
off.x += width - length;
}
@@ -804,7 +831,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
}
for (int j = 0; j < frame->lines.size(); j++) {
- _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs);
+ _draw_line(frame, j, p_ofs + rect.position + off + Vector2(0, frame->lines[j].offset.y), rect.size.x, p_base_color, p_outline_size, p_outline_color, p_font_shadow_color, p_shadow_outline_size, p_shadow_ofs, r_processed_glyphs);
}
idx++;
}
@@ -820,6 +847,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
Vector2 gloff = off;
// Draw oulines and shadow.
+ int processed_glyphs_ol = r_processed_glyphs;
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);
@@ -947,7 +975,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
// Draw glyph outlines.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
- if (frid != RID()) {
+ 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);
}
@@ -958,6 +987,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
TS->font_draw_glyph_outline(frid, ci, glyphs[i].font_size, size, p_ofs + fx_offset + gloff, gl, font_color);
}
}
+ processed_glyphs_ol++;
}
gloff.x += glyphs[i].advance;
}
@@ -996,7 +1026,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
float y_off = TS->shaped_text_get_underline_position(rid);
float underline_width = TS->shaped_text_get_underline_thickness(rid) * get_theme_default_base_scale();
draw_line(p_ofs + Vector2(off.x, off.y + y_off), p_ofs + Vector2(off.x + glyphs[i].advance * glyphs[i].repeat, off.y + y_off), uc, underline_width);
- } else if (_find_strikethrough(it)) {
+ }
+ if (_find_strikethrough(it)) {
Color uc = font_color;
uc.a *= 0.5;
float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2;
@@ -1124,11 +1155,15 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
// Draw glyphs.
for (int j = 0; j < glyphs[i].repeat; j++) {
if (visible) {
- if (frid != RID()) {
- TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
- } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
- TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ bool skip = (trim_chars && l.char_offset + glyphs[i].end > visible_characters) || (trim_glyphs_ltr && (r_processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (r_processed_glyphs < total_glyphs - visible_glyphs));
+ if (!skip) {
+ if (frid != RID()) {
+ TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) {
+ TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color);
+ }
}
+ r_processed_glyphs++;
}
off.x += glyphs[i].advance;
}
@@ -1214,17 +1249,17 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
}
- switch (l.text_buf->get_align()) {
- case HALIGN_FILL:
- case HALIGN_LEFT: {
+ switch (l.text_buf->get_alignment()) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
off.x += width - length;
}
} break;
- case HALIGN_CENTER: {
+ case HORIZONTAL_ALIGNMENT_CENTER: {
off.x += Math::floor((width - length) / 2.0);
} break;
- case HALIGN_RIGHT: {
+ case HORIZONTAL_ALIGNMENT_RIGHT: {
if (!rtl) {
off.x += width - length;
}
@@ -1423,7 +1458,7 @@ void RichTextLabel::_notification(int p_what) {
} break;
case NOTIFICATION_THEME_CHANGED:
case NOTIFICATION_ENTER_TREE: {
- if (text != "") {
+ if (!text.is_empty()) {
set_text(text);
}
@@ -1481,9 +1516,10 @@ void RichTextLabel::_notification(int p_what) {
// New cache draw.
Point2 ofs = text_rect.get_position() + Vector2(0, main->lines[from_line].offset.y - vofs);
+ int processed_glyphs = 0;
while (ofs.y < size.height && from_line < main->lines.size()) {
visible_paragraph_count++;
- visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs);
+ visible_line_count += _draw_line(main, from_line, ofs, text_rect.size.x, base_color, outline_size, outline_color, font_shadow_color, shadow_outline_size, shadow_ofs, processed_glyphs);
ofs.y += main->lines[from_line].text_buf->get_size().y + get_theme_constant(SNAME("line_separation"));
from_line++;
}
@@ -1950,19 +1986,19 @@ int RichTextLabel::_find_margin(Item *p_item, const Ref<Font> &p_base_font, int
return margin;
}
-RichTextLabel::Align RichTextLabel::_find_align(Item *p_item) {
+HorizontalAlignment RichTextLabel::_find_alignment(Item *p_item) {
Item *item = p_item;
while (item) {
if (item->type == ITEM_PARAGRAPH) {
ItemParagraph *p = static_cast<ItemParagraph *>(item);
- return p->align;
+ return p->alignment;
}
item = item->parent;
}
- return default_align;
+ return default_alignment;
}
TextServer::Direction RichTextLabel::_find_direction(Item *p_item) {
@@ -2161,10 +2197,6 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
}
// Resize lines without reshaping.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
@@ -2185,21 +2217,17 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
if (fit_content_height) {
- minimum_size_changed();
+ update_minimum_size();
}
return;
}
// Shape invalid lines.
- Size2 size = get_size();
- if (fixed_width != -1) {
- size.width = fixed_width;
- }
Rect2 text_rect = _get_text_rect();
Ref<Font> base_font = get_theme_font(SNAME("normal_font"));
@@ -2222,12 +2250,12 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
vscroll->set_max(total_height);
vscroll->set_page(text_rect.size.height);
if (scroll_follow && scroll_following) {
- vscroll->set_value(total_height - size.height);
+ vscroll->set_value(total_height);
}
updating_scroll = false;
if (fit_content_height) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2324,7 +2352,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline)
_invalidate_current_line(current_frame);
if (fixed_width != -1) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2349,9 +2377,10 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
// Then remove the provided item itself.
p_item->parent->subitems.erase(p_item);
}
+ memdelete(p_item);
}
-void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlign p_align) {
+void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment) {
if (current->type == ITEM_TABLE) {
return;
}
@@ -2363,7 +2392,7 @@ void RichTextLabel::add_image(const Ref<Texture2D> &p_image, const int p_width,
item->image = p_image;
item->color = p_color;
- item->inline_align = p_align;
+ item->inline_align = p_alignment;
if (p_width > 0) {
// custom width
@@ -2555,11 +2584,11 @@ void RichTextLabel::push_strikethrough() {
_add_item(item, true);
}
-void RichTextLabel::push_paragraph(Align p_align, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) {
+void RichTextLabel::push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction, const String &p_language, Control::StructuredTextParser p_st_parser) {
ERR_FAIL_COND(current->type == ITEM_TABLE);
ItemParagraph *item = memnew(ItemParagraph);
- item->align = p_align;
+ item->alignment = p_alignment;
item->direction = p_direction;
item->language = p_language;
item->st_parser = p_st_parser;
@@ -2595,13 +2624,13 @@ void RichTextLabel::push_meta(const Variant &p_meta) {
_add_item(item, true);
}
-void RichTextLabel::push_table(int p_columns, InlineAlign p_align) {
+void RichTextLabel::push_table(int p_columns, InlineAlignment p_alignment) {
ERR_FAIL_COND(p_columns < 1);
ItemTable *item = memnew(ItemTable);
item->columns.resize(p_columns);
item->total_width = 0;
- item->inline_align = p_align;
+ item->inline_align = p_alignment;
for (int i = 0; i < item->columns.size(); i++) {
item->columns.write[i].expand = false;
item->columns.write[i].expand_ratio = 1;
@@ -2755,7 +2784,7 @@ void RichTextLabel::clear() {
}
if (fixed_width != -1) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2772,7 +2801,7 @@ int RichTextLabel::get_tab_size() const {
void RichTextLabel::set_fit_content_height(bool p_enabled) {
if (p_enabled != fit_content_height) {
fit_content_height = p_enabled;
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -2958,35 +2987,35 @@ void RichTextLabel::append_text(const String &p_bbcode) {
columns = 1;
}
- int align = INLINE_ALIGN_TOP;
+ int alignment = INLINE_ALIGNMENT_TOP;
if (subtag.size() > 2) {
if (subtag[1] == "top" || subtag[1] == "t") {
- align = INLINE_ALIGN_TOP_TO;
+ alignment = INLINE_ALIGNMENT_TOP_TO;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align = INLINE_ALIGN_CENTER_TO;
+ alignment = INLINE_ALIGNMENT_CENTER_TO;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align = INLINE_ALIGN_BOTTOM_TO;
+ alignment = INLINE_ALIGNMENT_BOTTOM_TO;
}
if (subtag[2] == "top" || subtag[2] == "t") {
- align |= INLINE_ALIGN_TO_TOP;
+ alignment |= INLINE_ALIGNMENT_TO_TOP;
} else if (subtag[2] == "center" || subtag[2] == "c") {
- align |= INLINE_ALIGN_TO_CENTER;
+ alignment |= INLINE_ALIGNMENT_TO_CENTER;
} else if (subtag[2] == "baseline" || subtag[2] == "l") {
- align |= INLINE_ALIGN_TO_BASELINE;
+ alignment |= INLINE_ALIGNMENT_TO_BASELINE;
} else if (subtag[2] == "bottom" || subtag[2] == "b") {
- align |= INLINE_ALIGN_TO_BOTTOM;
+ alignment |= INLINE_ALIGNMENT_TO_BOTTOM;
}
} else if (subtag.size() > 1) {
if (subtag[1] == "top" || subtag[1] == "t") {
- align = INLINE_ALIGN_TOP;
+ alignment = INLINE_ALIGNMENT_TOP;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align = INLINE_ALIGN_CENTER;
+ alignment = INLINE_ALIGNMENT_CENTER;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align = INLINE_ALIGN_BOTTOM;
+ alignment = INLINE_ALIGNMENT_BOTTOM;
}
}
- push_table(columns, (InlineAlign)align);
+ push_table(columns, (InlineAlignment)alignment);
pos = brk_end + 1;
tag_stack.push_front("table");
} else if (tag == "cell") {
@@ -3050,6 +3079,12 @@ void RichTextLabel::append_text(const String &p_bbcode) {
push_strikethrough();
pos = brk_end + 1;
tag_stack.push_front(tag);
+ } else if (tag == "lb") {
+ add_text("[");
+ pos = brk_end + 1;
+ } else if (tag == "rb") {
+ add_text("]");
+ pos = brk_end + 1;
} else if (tag == "lrm") {
add_text(String::chr(0x200E));
pos = brk_end + 1;
@@ -3099,15 +3134,15 @@ void RichTextLabel::append_text(const String &p_bbcode) {
add_text(String::chr(0x00AD));
pos = brk_end + 1;
} else if (tag == "center") {
- push_paragraph(ALIGN_CENTER);
+ push_paragraph(HORIZONTAL_ALIGNMENT_CENTER);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "fill") {
- push_paragraph(ALIGN_FILL);
+ push_paragraph(HORIZONTAL_ALIGNMENT_FILL);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "right") {
- push_paragraph(ALIGN_RIGHT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_RIGHT);
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "ul") {
@@ -3146,12 +3181,12 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = brk_end + 1;
tag_stack.push_front(tag);
} else if (tag == "p") {
- push_paragraph(ALIGN_LEFT);
+ push_paragraph(HORIZONTAL_ALIGNMENT_LEFT);
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag.begins_with("p ")) {
Vector<String> subtag = tag.substr(2, tag.length()).split(" ");
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
Control::TextDirection dir = Control::TEXT_DIRECTION_INHERITED;
String lang;
Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
@@ -3160,13 +3195,13 @@ void RichTextLabel::append_text(const String &p_bbcode) {
if (subtag_a.size() == 2) {
if (subtag_a[0] == "align") {
if (subtag_a[1] == "l" || subtag_a[1] == "left") {
- align = ALIGN_LEFT;
+ alignment = HORIZONTAL_ALIGNMENT_LEFT;
} else if (subtag_a[1] == "c" || subtag_a[1] == "center") {
- align = ALIGN_CENTER;
+ alignment = HORIZONTAL_ALIGNMENT_CENTER;
} else if (subtag_a[1] == "r" || subtag_a[1] == "right") {
- align = ALIGN_RIGHT;
+ alignment = HORIZONTAL_ALIGNMENT_RIGHT;
} else if (subtag_a[1] == "f" || subtag_a[1] == "fill") {
- align = ALIGN_FILL;
+ alignment = HORIZONTAL_ALIGNMENT_FILL;
}
} else if (subtag_a[0] == "dir" || subtag_a[0] == "direction") {
if (subtag_a[1] == "a" || subtag_a[1] == "auto") {
@@ -3197,7 +3232,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
}
- push_paragraph(align, dir, lang, st_parser);
+ push_paragraph(alignment, dir, lang, st_parser);
pos = brk_end + 1;
tag_stack.push_front("p");
} else if (tag == "url") {
@@ -3265,33 +3300,33 @@ void RichTextLabel::append_text(const String &p_bbcode) {
pos = end;
tag_stack.push_front(bbcode_name);
} else if (tag.begins_with("img")) {
- int align = INLINE_ALIGN_CENTER;
+ int alignment = INLINE_ALIGNMENT_CENTER;
if (tag.begins_with("img=")) {
Vector<String> subtag = tag.substr(4, tag.length()).split(",");
if (subtag.size() > 1) {
if (subtag[0] == "top" || subtag[0] == "t") {
- align = INLINE_ALIGN_TOP_TO;
+ alignment = INLINE_ALIGNMENT_TOP_TO;
} else if (subtag[0] == "center" || subtag[0] == "c") {
- align = INLINE_ALIGN_CENTER_TO;
+ alignment = INLINE_ALIGNMENT_CENTER_TO;
} else if (subtag[0] == "bottom" || subtag[0] == "b") {
- align = INLINE_ALIGN_BOTTOM_TO;
+ alignment = INLINE_ALIGNMENT_BOTTOM_TO;
}
if (subtag[1] == "top" || subtag[1] == "t") {
- align |= INLINE_ALIGN_TO_TOP;
+ alignment |= INLINE_ALIGNMENT_TO_TOP;
} else if (subtag[1] == "center" || subtag[1] == "c") {
- align |= INLINE_ALIGN_TO_CENTER;
+ alignment |= INLINE_ALIGNMENT_TO_CENTER;
} else if (subtag[1] == "baseline" || subtag[1] == "l") {
- align |= INLINE_ALIGN_TO_BASELINE;
+ alignment |= INLINE_ALIGNMENT_TO_BASELINE;
} else if (subtag[1] == "bottom" || subtag[1] == "b") {
- align |= INLINE_ALIGN_TO_BOTTOM;
+ alignment |= INLINE_ALIGNMENT_TO_BOTTOM;
}
} else if (subtag.size() > 0) {
if (subtag[0] == "top" || subtag[0] == "t") {
- align = INLINE_ALIGN_TOP;
+ alignment = INLINE_ALIGNMENT_TOP;
} else if (subtag[0] == "center" || subtag[0] == "c") {
- align = INLINE_ALIGN_CENTER;
+ alignment = INLINE_ALIGNMENT_CENTER;
} else if (subtag[0] == "bottom" || subtag[0] == "b") {
- align = INLINE_ALIGN_BOTTOM;
+ alignment = INLINE_ALIGNMENT_BOTTOM;
}
}
}
@@ -3333,7 +3368,7 @@ void RichTextLabel::append_text(const String &p_bbcode) {
}
}
- add_image(texture, width, height, color, (InlineAlign)align);
+ add_image(texture, width, height, color, (InlineAlignment)alignment);
}
pos = end;
@@ -3853,7 +3888,7 @@ String RichTextLabel::get_selected_text() const {
void RichTextLabel::selection_copy() {
String text = get_selected_text();
- if (text != "") {
+ if (!text.is_empty()) {
DisplayServer::get_singleton()->clipboard_set(text);
}
}
@@ -3993,8 +4028,10 @@ void RichTextLabel::set_percent_visible(float p_percent) {
visible_characters = get_total_character_count() * p_percent;
percent_visible = p_percent;
}
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ }
update();
}
}
@@ -4005,7 +4042,7 @@ float RichTextLabel::get_percent_visible() const {
void RichTextLabel::set_effects(Array p_effects) {
custom_effects = p_effects;
- if ((text != "") && use_bbcode) {
+ if ((!text.is_empty()) && use_bbcode) {
parse_bbcode(text);
}
}
@@ -4020,7 +4057,7 @@ void RichTextLabel::install_effect(const Variant effect) {
if (rteffect.is_valid()) {
custom_effects.push_back(effect);
- if ((text != "") && use_bbcode) {
+ if ((!text.is_empty()) && use_bbcode) {
parse_bbcode(text);
}
}
@@ -4036,7 +4073,7 @@ int RichTextLabel::get_content_height() const {
#ifndef DISABLE_DEPRECATED
// People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0)
-// Altough some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty
+// Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty.
bool RichTextLabel::_set(const StringName &p_name, const Variant &p_value) {
if (p_name == "bbcode_text" && !((String)p_value).is_empty()) {
set_text(p_value);
@@ -4050,7 +4087,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_parsed_text"), &RichTextLabel::get_parsed_text);
ClassDB::bind_method(D_METHOD("add_text", "text"), &RichTextLabel::add_text);
ClassDB::bind_method(D_METHOD("set_text", "text"), &RichTextLabel::set_text);
- ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGN_CENTER));
+ ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER));
ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline);
ClassDB::bind_method(D_METHOD("remove_line", "line"), &RichTextLabel::remove_line);
ClassDB::bind_method(D_METHOD("push_font", "font"), &RichTextLabel::push_font);
@@ -4064,13 +4101,13 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("push_color", "color"), &RichTextLabel::push_color);
ClassDB::bind_method(D_METHOD("push_outline_size", "outline_size"), &RichTextLabel::push_outline_size);
ClassDB::bind_method(D_METHOD("push_outline_color", "color"), &RichTextLabel::push_outline_color);
- ClassDB::bind_method(D_METHOD("push_paragraph", "align", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT));
+ ClassDB::bind_method(D_METHOD("push_paragraph", "alignment", "base_direction", "language", "st_parser"), &RichTextLabel::push_paragraph, DEFVAL(TextServer::DIRECTION_AUTO), DEFVAL(""), DEFVAL(STRUCTURED_TEXT_DEFAULT));
ClassDB::bind_method(D_METHOD("push_indent", "level"), &RichTextLabel::push_indent);
ClassDB::bind_method(D_METHOD("push_list", "level", "type", "capitalize"), &RichTextLabel::push_list);
ClassDB::bind_method(D_METHOD("push_meta", "data"), &RichTextLabel::push_meta);
ClassDB::bind_method(D_METHOD("push_underline"), &RichTextLabel::push_underline);
ClassDB::bind_method(D_METHOD("push_strikethrough"), &RichTextLabel::push_strikethrough);
- ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGN_TOP));
+ ClassDB::bind_method(D_METHOD("push_table", "columns", "inline_align"), &RichTextLabel::push_table, DEFVAL(INLINE_ALIGNMENT_TOP));
ClassDB::bind_method(D_METHOD("push_dropcap", "string", "font", "size", "dropcap_margins", "color", "outline_size", "outline_color"), &RichTextLabel::push_dropcap, DEFVAL(Rect2()), DEFVAL(Color(1, 1, 1)), DEFVAL(0), DEFVAL(Color(0, 0, 0, 0)));
ClassDB::bind_method(D_METHOD("set_table_column_expand", "column", "expand", "ratio"), &RichTextLabel::set_table_column_expand);
ClassDB::bind_method(D_METHOD("set_cell_row_background_color", "odd_row_bg", "even_row_bg"), &RichTextLabel::set_cell_row_background_color);
@@ -4105,7 +4142,7 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scroll_follow", "follow"), &RichTextLabel::set_scroll_follow);
ClassDB::bind_method(D_METHOD("is_scroll_following"), &RichTextLabel::is_scroll_following);
- ClassDB::bind_method(D_METHOD("get_v_scroll"), &RichTextLabel::get_v_scroll);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &RichTextLabel::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("scroll_to_line", "line"), &RichTextLabel::scroll_to_line);
ClassDB::bind_method(D_METHOD("scroll_to_paragraph", "paragraph"), &RichTextLabel::scroll_to_paragraph);
@@ -4135,6 +4172,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_visible_characters", "amount"), &RichTextLabel::set_visible_characters);
ClassDB::bind_method(D_METHOD("get_visible_characters"), &RichTextLabel::get_visible_characters);
+ ClassDB::bind_method(D_METHOD("get_visible_characters_behavior"), &RichTextLabel::get_visible_characters_behavior);
+ ClassDB::bind_method(D_METHOD("set_visible_characters_behavior", "behavior"), &RichTextLabel::set_visible_characters_behavior);
+
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);
@@ -4160,6 +4200,8 @@ void RichTextLabel::_bind_methods() {
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");
+
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");
@@ -4178,7 +4220,7 @@ void RichTextLabel::_bind_methods() {
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::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"), "set_language", "get_language");
+ 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");
@@ -4188,11 +4230,6 @@ 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(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_FILL);
-
BIND_ENUM_CONSTANT(LIST_NUMBERS);
BIND_ENUM_CONSTANT(LIST_LETTERS);
BIND_ENUM_CONSTANT(LIST_ROMAN);
@@ -4224,6 +4261,25 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(ITEM_META);
BIND_ENUM_CONSTANT(ITEM_DROPCAP);
BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
+
+ BIND_ENUM_CONSTANT(VC_CHARS_BEFORE_SHAPING);
+ BIND_ENUM_CONSTANT(VC_CHARS_AFTER_SHAPING);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_AUTO);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_LTR);
+ BIND_ENUM_CONSTANT(VC_GLYPHS_RTL);
+}
+
+RichTextLabel::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {
+ return visible_chars_behavior;
+}
+
+void RichTextLabel::set_visible_characters_behavior(RichTextLabel::VisibleCharactersBehavior p_behavior) {
+ if (visible_chars_behavior != p_behavior) {
+ visible_chars_behavior = p_behavior;
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ update();
+ }
}
void RichTextLabel::set_visible_characters(int p_visible) {
@@ -4237,8 +4293,10 @@ void RichTextLabel::set_visible_characters(int p_visible) {
percent_visible = (float)p_visible / (float)total_char_count;
}
}
- main->first_invalid_line = 0; //invalidate ALL
- _validate_line_caches(main);
+ if (visible_chars_behavior == VC_CHARS_BEFORE_SHAPING) {
+ main->first_invalid_line = 0; //invalidate ALL
+ _validate_line_caches(main);
+ }
update();
}
}
@@ -4266,9 +4324,25 @@ int RichTextLabel::get_total_character_count() const {
return tc;
}
+int RichTextLabel::get_total_glyph_count() const {
+ int tg = 0;
+ Item *it = main;
+ while (it) {
+ if (it->type == ITEM_FRAME) {
+ ItemFrame *f = static_cast<ItemFrame *>(it);
+ for (int i = 0; i < f->lines.size(); i++) {
+ tg += TS->shaped_text_get_glyph_count(f->lines[i].text_buf->get_rid());
+ }
+ }
+ it = _get_next_item(it, true);
+ }
+
+ return tg;
+}
+
void RichTextLabel::set_fixed_size_to_width(int p_width) {
fixed_width = p_width;
- minimum_size_changed();
+ update_minimum_size();
}
Size2 RichTextLabel::get_minimum_size() const {
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 5b58f14d96..70467e7e7c 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,13 +39,6 @@ class RichTextLabel : public Control {
GDCLASS(RichTextLabel, Control);
public:
- enum Align {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_FILL
- };
-
enum ListType {
LIST_NUMBERS,
LIST_LETTERS,
@@ -82,6 +75,14 @@ public:
ITEM_CUSTOMFX
};
+ enum VisibleCharactersBehavior {
+ VC_CHARS_BEFORE_SHAPING,
+ VC_CHARS_AFTER_SHAPING,
+ VC_GLYPHS_AUTO,
+ VC_GLYPHS_LTR,
+ VC_GLYPHS_RTL,
+ };
+
protected:
void _notification(int p_what);
static void _bind_methods();
@@ -160,7 +161,7 @@ private:
struct ItemImage : public Item {
Ref<Texture2D> image;
- InlineAlign inline_align = INLINE_ALIGN_CENTER;
+ InlineAlignment inline_align = INLINE_ALIGNMENT_CENTER;
Size2 size;
Color color;
ItemImage() { type = ITEM_IMAGE; }
@@ -210,7 +211,7 @@ private:
};
struct ItemParagraph : public Item {
- Align align = ALIGN_LEFT;
+ HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT;
String language;
Control::TextDirection direction = Control::TEXT_DIRECTION_AUTO;
Control::StructuredTextParser st_parser = STRUCTURED_TEXT_DEFAULT;
@@ -247,7 +248,7 @@ private:
int total_width = 0;
int total_height = 0;
- InlineAlign inline_align = INLINE_ALIGN_TOP;
+ InlineAlignment inline_align = INLINE_ALIGNMENT_TOP;
ItemTable() { type = ITEM_TABLE; }
};
@@ -360,7 +361,7 @@ private:
bool underline_meta = true;
bool override_selected_font_color = false;
- Align default_align = ALIGN_LEFT;
+ HorizontalAlignment default_alignment = HORIZONTAL_ALIGNMENT_LEFT;
ItemMeta *meta_hovering = nullptr;
Variant current_meta;
@@ -403,6 +404,7 @@ private:
int visible_characters = -1;
float percent_visible = 1.0;
+ VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING;
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);
@@ -412,7 +414,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);
- 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 _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);
String _roman(int p_num, bool p_capitalize) const;
@@ -428,7 +430,7 @@ private:
ItemDropcap *_find_dc_item(Item *p_item);
int _find_list(Item *p_item, Vector<int> &r_index, Vector<ItemList *> &r_list);
int _find_margin(Item *p_item, const Ref<Font> &p_base_font, int p_base_font_size);
- Align _find_align(Item *p_item);
+ HorizontalAlignment _find_alignment(Item *p_item);
TextServer::Direction _find_direction(Item *p_item);
Control::StructuredTextParser _find_stt(Item *p_item);
String _find_language(Item *p_item);
@@ -469,7 +471,7 @@ private:
public:
String get_parsed_text() const;
void add_text(const String &p_text);
- void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlign p_align = INLINE_ALIGN_CENTER);
+ void add_image(const Ref<Texture2D> &p_image, const int p_width = 0, const int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER);
void add_newline();
bool remove_line(const int p_line);
void push_dropcap(const String &p_string, const Ref<Font> &p_font, int p_size, const Rect2 &p_dropcap_margins = Rect2(), const Color &p_color = Color(1, 1, 1), int p_ol_size = 0, const Color &p_ol_color = Color(0, 0, 0, 0));
@@ -486,11 +488,11 @@ public:
void push_outline_color(const Color &p_color);
void push_underline();
void push_strikethrough();
- void push_paragraph(Align p_align, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT);
+ void push_paragraph(HorizontalAlignment p_alignment, Control::TextDirection p_direction = Control::TEXT_DIRECTION_INHERITED, const String &p_language = "", Control::StructuredTextParser p_st_parser = STRUCTURED_TEXT_DEFAULT);
void push_indent(int p_level);
void push_list(int p_level, ListType p_list, bool p_capitalize);
void push_meta(const Variant &p_meta);
- void push_table(int p_columns, InlineAlign p_align = INLINE_ALIGN_TOP);
+ void push_table(int p_columns, InlineAlignment p_alignment = INLINE_ALIGNMENT_TOP);
void push_fade(int p_start_index, int p_length);
void push_shake(int p_strength, float p_rate);
void push_wave(float p_frequency, float p_amplitude);
@@ -542,7 +544,7 @@ public:
int get_content_height() const;
- VScrollBar *get_v_scroll() { return vscroll; }
+ VScrollBar *get_v_scroll_bar() { return vscroll; }
virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override;
@@ -579,10 +581,14 @@ public:
void set_visible_characters(int p_visible);
int get_visible_characters() const;
int get_total_character_count() const;
+ int get_total_glyph_count() const;
void set_percent_visible(float p_percent);
float get_percent_visible() const;
+ VisibleCharactersBehavior get_visible_characters_behavior() const;
+ void set_visible_characters_behavior(VisibleCharactersBehavior p_behavior);
+
void set_effects(Array p_effects);
Array get_effects();
@@ -595,8 +601,8 @@ public:
~RichTextLabel();
};
-VARIANT_ENUM_CAST(RichTextLabel::Align);
VARIANT_ENUM_CAST(RichTextLabel::ListType);
VARIANT_ENUM_CAST(RichTextLabel::ItemType);
+VARIANT_ENUM_CAST(RichTextLabel::VisibleCharactersBehavior);
#endif // RICH_TEXT_LABEL_H
diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp
index 8c292e663e..343056957c 100644
--- a/scene/gui/scroll_bar.cpp
+++ b/scene/gui/scroll_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h
index 574d17ee20..651edd1a74 100644
--- a/scene/gui/scroll_bar.h
+++ b/scene/gui/scroll_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 7b2ea46e17..5e128d594c 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -49,10 +49,10 @@ Size2 ScrollContainer::get_minimum_size() const {
}
Size2 minsize = c->get_combined_minimum_size();
- if (!scroll_h) {
+ if (horizontal_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.x = MAX(min_size.x, minsize.x);
}
- if (!scroll_v) {
+ if (vertical_scroll_mode == SCROLL_MODE_DISABLED) {
min_size.y = MAX(min_size.y, minsize.y);
}
}
@@ -170,7 +170,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
Vector2 motion = mm->get_relative();
drag_accum -= motion;
- if (beyond_deadzone || (scroll_h && Math::abs(drag_accum.x) > deadzone) || (scroll_v && Math::abs(drag_accum.y) > deadzone)) {
+ if (beyond_deadzone || (horizontal_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.x) > deadzone) || (vertical_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.y) > deadzone)) {
if (!beyond_deadzone) {
propagate_notification(NOTIFICATION_SCROLL_BEGIN);
emit_signal(SNAME("scroll_started"));
@@ -180,12 +180,12 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) {
drag_accum = -motion;
}
Vector2 diff = drag_from + drag_accum;
- if (scroll_h) {
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
h_scroll->set_value(diff.x);
} else {
drag_accum.x = 0;
}
- if (scroll_v) {
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
v_scroll->set_value(diff.y);
} else {
drag_accum.y = 0;
@@ -286,7 +286,7 @@ void ScrollContainer::_update_dimensions() {
child_max_size.y = MAX(child_max_size.y, minsize.y);
Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize);
- if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
+ if (horizontal_scroll_mode == SCROLL_MODE_DISABLED || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
r.position.x = 0;
if (c->get_h_size_flags() & SIZE_EXPAND) {
r.size.width = MAX(size.width, minsize.width);
@@ -294,7 +294,7 @@ void ScrollContainer::_update_dimensions() {
r.size.width = minsize.width;
}
}
- if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
+ if (vertical_scroll_mode == SCROLL_MODE_DISABLED || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
r.position.y = 0;
if (c->get_v_size_flags() & SIZE_EXPAND) {
r.size.height = MAX(size.height, minsize.height);
@@ -364,10 +364,10 @@ void ScrollContainer::_notification(int p_what) {
turnoff_v = true;
}
- if (scroll_h) {
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
h_scroll->set_value(pos.x);
}
- if (scroll_v) {
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
v_scroll->set_value(pos.y);
}
@@ -413,17 +413,17 @@ void ScrollContainer::update_scrollbars() {
Size2 hmin;
Size2 vmin;
- if (scroll_h) {
+ if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) {
hmin = h_scroll->get_combined_minimum_size();
}
- if (scroll_v) {
+ if (vertical_scroll_mode != SCROLL_MODE_DISABLED) {
vmin = v_scroll->get_combined_minimum_size();
}
Size2 min = child_max_size;
- bool hide_scroll_h = !scroll_h || min.width <= size.width || !h_scroll_visible;
- bool hide_scroll_v = !scroll_v || min.height <= size.height || !v_scroll_visible;
+ bool hide_scroll_h = horizontal_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (horizontal_scroll_mode == SCROLL_MODE_DISABLED || horizontal_scroll_mode == SCROLL_MODE_SHOW_NEVER || (horizontal_scroll_mode == SCROLL_MODE_AUTO && min.width <= size.width));
+ bool hide_scroll_v = vertical_scroll_mode != SCROLL_MODE_SHOW_ALWAYS && (vertical_scroll_mode == SCROLL_MODE_DISABLED || vertical_scroll_mode == SCROLL_MODE_SHOW_NEVER || (vertical_scroll_mode == SCROLL_MODE_AUTO && min.height <= size.height));
h_scroll->set_max(min.width);
h_scroll->set_page(size.width - (hide_scroll_v ? 0 : vmin.width));
@@ -461,58 +461,32 @@ int ScrollContainer::get_v_scroll() const {
return v_scroll->get_value();
}
-void ScrollContainer::set_enable_h_scroll(bool p_enable) {
- if (scroll_h == p_enable) {
+void ScrollContainer::set_horizontal_scroll_mode(ScrollMode p_mode) {
+ if (horizontal_scroll_mode == p_mode) {
return;
}
- scroll_h = p_enable;
- minimum_size_changed();
+ horizontal_scroll_mode = p_mode;
+ update_minimum_size();
queue_sort();
}
-bool ScrollContainer::is_h_scroll_enabled() const {
- return scroll_h;
+ScrollContainer::ScrollMode ScrollContainer::get_horizontal_scroll_mode() const {
+ return horizontal_scroll_mode;
}
-void ScrollContainer::set_enable_v_scroll(bool p_enable) {
- if (scroll_v == p_enable) {
+void ScrollContainer::set_vertical_scroll_mode(ScrollMode p_mode) {
+ if (vertical_scroll_mode == p_mode) {
return;
}
- scroll_v = p_enable;
- minimum_size_changed();
+ vertical_scroll_mode = p_mode;
+ update_minimum_size();
queue_sort();
}
-bool ScrollContainer::is_v_scroll_enabled() const {
- return scroll_v;
-}
-
-void ScrollContainer::set_h_scroll_visible(bool p_visible) {
- if (h_scroll_visible == p_visible) {
- return;
- }
-
- h_scroll_visible = p_visible;
- update_scrollbars();
-}
-
-bool ScrollContainer::is_h_scroll_visible() const {
- return h_scroll_visible;
-}
-
-void ScrollContainer::set_v_scroll_visible(bool p_visible) {
- if (v_scroll_visible == p_visible) {
- return;
- }
-
- v_scroll_visible = p_visible;
- update_scrollbars();
-}
-
-bool ScrollContainer::is_v_scroll_visible() const {
- return v_scroll_visible;
+ScrollContainer::ScrollMode ScrollContainer::get_vertical_scroll_mode() const {
+ return vertical_scroll_mode;
}
int ScrollContainer::get_deadzone() const {
@@ -558,11 +532,11 @@ TypedArray<String> ScrollContainer::get_configuration_warnings() const {
return warnings;
}
-HScrollBar *ScrollContainer::get_h_scrollbar() {
+HScrollBar *ScrollContainer::get_h_scroll_bar() {
return h_scroll;
}
-VScrollBar *ScrollContainer::get_v_scrollbar() {
+VScrollBar *ScrollContainer::get_v_scroll_bar() {
return v_scroll;
}
@@ -575,17 +549,11 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll);
ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll);
- ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll);
- ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled);
-
- ClassDB::bind_method(D_METHOD("set_enable_v_scroll", "enable"), &ScrollContainer::set_enable_v_scroll);
- ClassDB::bind_method(D_METHOD("is_v_scroll_enabled"), &ScrollContainer::is_v_scroll_enabled);
+ ClassDB::bind_method(D_METHOD("set_horizontal_scroll_mode", "enable"), &ScrollContainer::set_horizontal_scroll_mode);
+ ClassDB::bind_method(D_METHOD("get_horizontal_scroll_mode"), &ScrollContainer::get_horizontal_scroll_mode);
- ClassDB::bind_method(D_METHOD("set_h_scroll_visible", "visible"), &ScrollContainer::set_h_scroll_visible);
- ClassDB::bind_method(D_METHOD("is_h_scroll_visible"), &ScrollContainer::is_h_scroll_visible);
-
- ClassDB::bind_method(D_METHOD("set_v_scroll_visible", "visible"), &ScrollContainer::set_v_scroll_visible);
- ClassDB::bind_method(D_METHOD("is_v_scroll_visible"), &ScrollContainer::is_v_scroll_visible);
+ ClassDB::bind_method(D_METHOD("set_vertical_scroll_mode", "enable"), &ScrollContainer::set_vertical_scroll_mode);
+ ClassDB::bind_method(D_METHOD("get_vertical_scroll_mode"), &ScrollContainer::get_vertical_scroll_mode);
ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone);
ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone);
@@ -593,8 +561,8 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_follow_focus", "enabled"), &ScrollContainer::set_follow_focus);
ClassDB::bind_method(D_METHOD("is_following_focus"), &ScrollContainer::is_following_focus);
- ClassDB::bind_method(D_METHOD("get_h_scrollbar"), &ScrollContainer::get_h_scrollbar);
- ClassDB::bind_method(D_METHOD("get_v_scrollbar"), &ScrollContainer::get_v_scrollbar);
+ ClassDB::bind_method(D_METHOD("get_h_scroll_bar"), &ScrollContainer::get_h_scroll_bar);
+ ClassDB::bind_method(D_METHOD("get_v_scroll_bar"), &ScrollContainer::get_v_scroll_bar);
ClassDB::bind_method(D_METHOD("ensure_control_visible", "control"), &ScrollContainer::ensure_control_visible);
ADD_SIGNAL(MethodInfo("scroll_started"));
@@ -605,12 +573,15 @@ void ScrollContainer::_bind_methods() {
ADD_GROUP("Scroll", "scroll_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_visible"), "set_h_scroll_visible", "is_h_scroll_visible");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_visible"), "set_v_scroll_visible", "is_v_scroll_visible");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_horizontal_scroll_mode", "get_horizontal_scroll_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_scroll_mode", PROPERTY_HINT_ENUM, "Disabled,Auto,Always Show,Never Show"), "set_vertical_scroll_mode", "get_vertical_scroll_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone");
+ BIND_ENUM_CONSTANT(SCROLL_MODE_DISABLED);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_AUTO);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_ALWAYS);
+ BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER);
+
GLOBAL_DEF("gui/common/default_scroll_deadzone", 0);
};
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index 9f4ec558dc..c00df87b18 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,6 +38,15 @@
class ScrollContainer : public Container {
GDCLASS(ScrollContainer, Container);
+public:
+ enum ScrollMode {
+ SCROLL_MODE_DISABLED = 0,
+ SCROLL_MODE_AUTO,
+ SCROLL_MODE_SHOW_ALWAYS,
+ SCROLL_MODE_SHOW_NEVER,
+ };
+
+private:
HScrollBar *h_scroll;
VScrollBar *v_scroll;
@@ -54,11 +63,8 @@ class ScrollContainer : public Container {
bool drag_touching_deaccel = false;
bool beyond_deadzone = false;
- bool scroll_h = true;
- bool scroll_v = true;
-
- bool h_scroll_visible = true;
- bool v_scroll_visible = true;
+ ScrollMode horizontal_scroll_mode = SCROLL_MODE_AUTO;
+ ScrollMode vertical_scroll_mode = SCROLL_MODE_AUTO;
int deadzone = 0;
bool follow_focus = false;
@@ -68,7 +74,6 @@ class ScrollContainer : public Container {
protected:
Size2 get_minimum_size() const override;
- virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
void _gui_focus_changed(Control *p_control);
void _update_dimensions();
void _notification(int p_what);
@@ -80,23 +85,19 @@ protected:
void _update_scrollbar_position();
public:
+ virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
+
void set_h_scroll(int p_pos);
int get_h_scroll() const;
void set_v_scroll(int p_pos);
int get_v_scroll() const;
- void set_enable_h_scroll(bool p_enable);
- bool is_h_scroll_enabled() const;
-
- void set_enable_v_scroll(bool p_enable);
- bool is_v_scroll_enabled() const;
+ void set_horizontal_scroll_mode(ScrollMode p_mode);
+ ScrollMode get_horizontal_scroll_mode() const;
- void set_h_scroll_visible(bool p_visible);
- bool is_h_scroll_visible() const;
-
- void set_v_scroll_visible(bool p_visible);
- bool is_v_scroll_visible() const;
+ void set_vertical_scroll_mode(ScrollMode p_mode);
+ ScrollMode get_vertical_scroll_mode() const;
int get_deadzone() const;
void set_deadzone(int p_deadzone);
@@ -104,8 +105,8 @@ public:
bool is_following_focus() const;
void set_follow_focus(bool p_follow);
- HScrollBar *get_h_scrollbar();
- VScrollBar *get_v_scrollbar();
+ HScrollBar *get_h_scroll_bar();
+ VScrollBar *get_v_scroll_bar();
void ensure_control_visible(Control *p_control);
TypedArray<String> get_configuration_warnings() const override;
@@ -113,4 +114,6 @@ public:
ScrollContainer();
};
+VARIANT_ENUM_CAST(ScrollContainer::ScrollMode);
+
#endif
diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp
index 1f3cb7aa24..9c19eb54dc 100644
--- a/scene/gui/separator.cpp
+++ b/scene/gui/separator.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/separator.h b/scene/gui/separator.h
index 77162c68fa..1621bb3351 100644
--- a/scene/gui/separator.h
+++ b/scene/gui/separator.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp
index 4cc425aad3..1d459d589f 100644
--- a/scene/gui/slider.cpp
+++ b/scene/gui/slider.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -70,8 +70,13 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
}
grab.active = true;
grab.uvalue = get_as_ratio();
+
+ emit_signal(SNAME("drag_started"));
} else {
grab.active = false;
+
+ const bool value_changed = !Math::is_equal_approx((double)grab.uvalue, get_as_ratio());
+ emit_signal(SNAME("drag_ended"), value_changed);
}
} else if (scrollable) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
@@ -142,7 +147,7 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) {
void Slider::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_MOUSE_ENTER: {
@@ -264,6 +269,9 @@ void Slider::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scrollable", "scrollable"), &Slider::set_scrollable);
ClassDB::bind_method(D_METHOD("is_scrollable"), &Slider::is_scrollable);
+ ADD_SIGNAL(MethodInfo("drag_started"));
+ ADD_SIGNAL(MethodInfo("drag_ended", PropertyInfo(Variant::BOOL, "value_changed")));
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scrollable"), "set_scrollable", "is_scrollable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tick_count", PROPERTY_HINT_RANGE, "0,4096,1"), "set_ticks", "get_ticks");
diff --git a/scene/gui/slider.h b/scene/gui/slider.h
index 46fa08bbf0..5fbfee2aec 100644
--- a/scene/gui/slider.h
+++ b/scene/gui/slider.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index f30206c943..19d47ea492 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -41,10 +41,10 @@ Size2 SpinBox::get_minimum_size() const {
void SpinBox::_value_changed(double) {
String value = TS->format_number(String::num(get_value(), Math::range_step_decimals(get_step())));
- if (prefix != "") {
+ if (!prefix.is_empty()) {
value = prefix + " " + value;
}
- if (suffix != "") {
+ if (!suffix.is_empty()) {
value += " " + suffix;
}
line_edit->set_text(value);
@@ -220,19 +220,19 @@ void SpinBox::_notification(int p_what) {
} else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
_value_changed(0);
} else if (p_what == NOTIFICATION_THEME_CHANGED) {
- call_deferred(SNAME("minimum_size_changed"));
- get_line_edit()->call_deferred(SNAME("minimum_size_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();
}
}
-void SpinBox::set_align(LineEdit::Align p_align) {
- line_edit->set_align(p_align);
+void SpinBox::set_horizontal_alignment(HorizontalAlignment p_alignment) {
+ line_edit->set_horizontal_alignment(p_alignment);
}
-LineEdit::Align SpinBox::get_align() const {
- return line_edit->get_align();
+HorizontalAlignment SpinBox::get_horizontal_alignment() const {
+ return line_edit->get_horizontal_alignment();
}
void SpinBox::set_suffix(const String &p_suffix) {
@@ -284,8 +284,8 @@ void SpinBox::apply() {
}
void SpinBox::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_align", "align"), &SpinBox::set_align);
- ClassDB::bind_method(D_METHOD("get_align"), &SpinBox::get_align);
+ ClassDB::bind_method(D_METHOD("set_horizontal_alignment", "alignment"), &SpinBox::set_horizontal_alignment);
+ ClassDB::bind_method(D_METHOD("get_horizontal_alignment"), &SpinBox::get_horizontal_alignment);
ClassDB::bind_method(D_METHOD("set_suffix", "suffix"), &SpinBox::set_suffix);
ClassDB::bind_method(D_METHOD("get_suffix"), &SpinBox::get_suffix);
ClassDB::bind_method(D_METHOD("set_prefix", "prefix"), &SpinBox::set_prefix);
@@ -297,7 +297,7 @@ void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix");
@@ -310,7 +310,7 @@ SpinBox::SpinBox() {
line_edit->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
line_edit->set_mouse_filter(MOUSE_FILTER_PASS);
- line_edit->set_align(LineEdit::ALIGN_LEFT);
+ line_edit->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
line_edit->connect("text_submitted", callable_mp(this, &SpinBox::_text_submitted), Vector<Variant>(), CONNECT_DEFERRED);
line_edit->connect("focus_exited", callable_mp(this, &SpinBox::_line_edit_focus_exit), Vector<Variant>(), CONNECT_DEFERRED);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index f2299ce1c2..0691a4b48d 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -79,8 +79,8 @@ public:
virtual Size2 get_minimum_size() const override;
- void set_align(LineEdit::Align p_align);
- LineEdit::Align get_align() const;
+ void set_horizontal_alignment(HorizontalAlignment p_alignment);
+ HorizontalAlignment get_horizontal_alignment() const;
void set_editable(bool p_enabled);
bool is_editable() const;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 6b53c0220e..874e5868b6 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -201,7 +201,7 @@ void SplitContainer::_notification(int p_what) {
}
} break;
case NOTIFICATION_THEME_CHANGED: {
- minimum_size_changed();
+ update_minimum_size();
} break;
}
}
diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h
index 47fd30a122..ba6fff6f55 100644
--- a/scene/gui/split_container.h
+++ b/scene/gui/split_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 53ea32e1b7..760144591e 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -54,6 +54,7 @@ Size2 SubViewportContainer::get_minimum_size() const {
void SubViewportContainer::set_stretch(bool p_enable) {
stretch = p_enable;
+ update_minimum_size();
queue_sort();
update();
}
diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h
index 7853f1590e..e7520763fb 100644
--- a/scene/gui/subviewport_container.h
+++ b/scene/gui/subviewport_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index 57ee7fd494..9da030f0a2 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -50,7 +50,7 @@ Size2 TabBar::get_minimum_size() const {
Ref<Texture2D> tex = tabs[i].icon;
if (tex.is_valid()) {
ms.height = MAX(ms.height, tex->get_size().height);
- if (tabs[i].text != "") {
+ if (!tabs[i].text.is_empty()) {
ms.width += get_theme_constant(SNAME("hseparation"));
}
}
@@ -147,6 +147,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (scrolling_enabled && buttons_visible) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
}
@@ -154,9 +155,9 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_pressed()) {
if (scrolling_enabled && buttons_visible) {
- if (missing_right) {
+ if (missing_right && offset < tabs.size()) {
offset++;
- _ensure_no_over_offset(); // Avoid overreaching when scrolling fast.
+ _update_cache();
update();
}
}
@@ -164,7 +165,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) {
- // pressed
+ // Right mouse button clicked.
emit_signal(SNAME("tab_rmb_clicked"), rb_hover);
}
@@ -174,7 +175,7 @@ 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) {
- // pressed
+ // Close button pressed.
emit_signal(SNAME("tab_close_pressed"), cb_hover);
}
@@ -183,7 +184,7 @@ 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
+ // Clicks.
Point2 pos = mb->get_position();
if (buttons_visible) {
@@ -194,12 +195,14 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x < decr->get_width()) {
if (missing_right) {
offset++;
+ _update_cache();
update();
}
return;
} else if (pos.x < incr->get_width() + decr->get_width()) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
return;
@@ -209,12 +212,14 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
if (pos.x > limit + decr->get_width()) {
if (missing_right) {
offset++;
+ _update_cache();
update();
}
return;
} else if (pos.x > limit) {
if (offset > 0) {
offset--;
+ _update_cache();
update();
}
return;
@@ -235,7 +240,7 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- if (tabs[i].cb_rect.has_point(pos)) {
+ if (tabs[i].cb_rect.has_point(pos) && (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current))) {
cb_pressing = true;
update();
return;
@@ -270,7 +275,7 @@ 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 != "") ? tabs[p_tab].language : TranslationServer::get_singleton()->get_tool_locale());
+ 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());
}
void TabBar::_notification(int p_what) {
@@ -285,7 +290,7 @@ void TabBar::_notification(int p_what) {
_shape(i);
}
_update_cache();
- minimum_size_changed();
+ update_minimum_size();
update();
} break;
case NOTIFICATION_RESIZED: {
@@ -294,8 +299,9 @@ void TabBar::_notification(int p_what) {
ensure_tab_visible(current);
} break;
case NOTIFICATION_DRAW: {
- _update_cache();
- RID ci = get_canvas_item();
+ if (tabs.is_empty()) {
+ return;
+ }
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
@@ -303,174 +309,46 @@ void TabBar::_notification(int p_what) {
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"));
- Ref<Texture2D> close = get_theme_icon(SNAME("close"));
- Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
- int outline_size = get_theme_constant(SNAME("outline_size"));
-
- Vector2 size = get_size();
- bool rtl = is_layout_rtl();
-
- int h = get_size().height;
- int w = 0;
- int mw = 0;
-
- for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = mw;
- mw += get_tab_width(i);
- }
-
- if (tab_align == ALIGN_CENTER) {
- w = (get_size().width - mw) / 2;
- } else if (tab_align == ALIGN_RIGHT) {
- w = get_size().width - mw;
- }
-
- if (w < 0) {
- w = 0;
- }
-
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
Ref<Texture2D> incr_hl = get_theme_icon(SNAME("increment_highlight"));
Ref<Texture2D> decr_hl = get_theme_icon(SNAME("decrement_highlight"));
- int limit = get_size().width;
- int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
-
- missing_right = false;
-
- for (int i = offset; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = w;
-
- int lsize = tabs[i].size_cache;
-
- Ref<StyleBox> sb;
- Color col;
-
- if (tabs[i].disabled) {
- sb = tab_disabled;
- col = font_disabled_color;
- } else if (i == current) {
- sb = tab_selected;
- col = font_selected_color;
- } else {
- sb = tab_unselected;
- col = font_unselected_color;
- }
-
- int new_width = w + lsize;
- if (new_width > limit || (i < tabs.size() - 1 && new_width > limit_minus_buttons)) { // For the last tab, we accept if the tab covers the buttons.
- max_drawn_tab = i - 1;
- missing_right = true;
- break;
- } else {
- max_drawn_tab = i;
- }
-
- Rect2 sb_rect;
- if (rtl) {
- sb_rect = Rect2(size.width - w - tabs[i].size_cache, 0, tabs[i].size_cache, h);
- } else {
- sb_rect = Rect2(w, 0, tabs[i].size_cache, h);
- }
- sb->draw(ci, sb_rect);
-
- w += sb->get_margin(SIDE_LEFT);
-
- Size2i sb_ms = sb->get_minimum_size();
- Ref<Texture2D> icon = tabs[i].icon;
- if (icon.is_valid()) {
- if (rtl) {
- icon->draw(ci, Point2i(size.width - w - icon->get_width(), sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- } else {
- icon->draw(ci, Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - icon->get_height()) / 2));
- }
- if (tabs[i].text != "") {
- w += icon->get_width() + get_theme_constant(SNAME("hseparation"));
- }
- }
-
- if (rtl) {
- Vector2 text_pos = Point2i(size.width - w - tabs[i].text_buf->get_size().x, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
- } else {
- Vector2 text_pos = Point2i(w, sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - tabs[i].text_buf->get_size().y) / 2);
- if (outline_size > 0 && font_outline_color.a > 0) {
- tabs[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color);
- }
- tabs[i].text_buf->draw(ci, text_pos, col);
- }
-
- w += tabs[i].size_text;
-
- if (tabs[i].right_button.is_valid()) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> rb = tabs[i].right_button;
+ bool rtl = is_layout_rtl();
+ Vector2 size = get_size();
+ int limit_minus_buttons = size.width - incr->get_width() - decr->get_width();
- w += get_theme_constant(SNAME("hseparation"));
+ int ofs = tabs[offset].ofs_cache;
- Rect2 rb_rect;
- rb_rect.size = style->get_minimum_size() + rb->get_size();
- if (rtl) {
- rb_rect.position.x = size.width - w - rb_rect.size.x;
+ // Draw unselected tabs in the back.
+ for (int i = offset; i <= max_drawn_tab; i++) {
+ if (i != current) {
+ Ref<StyleBox> sb;
+ Color col;
+
+ if (tabs[i].disabled) {
+ sb = tab_disabled;
+ col = font_disabled_color;
+ } else if (i == current) {
+ sb = tab_selected;
+ col = font_selected_color;
} else {
- rb_rect.position.x = w;
+ sb = tab_unselected;
+ col = font_unselected_color;
}
- rb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (rb_rect.size.y)) / 2;
- if (rb_hover == i) {
- if (rb_pressing) {
- get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
- } else {
- style->draw(ci, rb_rect);
- }
- }
-
- if (rtl) {
- rb->draw(ci, Point2i(size.width - w - rb_rect.size.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- rb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += rb->get_width();
- tabs.write[i].rb_rect = rb_rect;
+ _draw_tab(sb, col, i, rtl ? size.width - ofs - tabs[i].size_cache : ofs);
}
- if (cb_displaypolicy == CLOSE_BUTTON_SHOW_ALWAYS || (cb_displaypolicy == CLOSE_BUTTON_SHOW_ACTIVE_ONLY && i == current)) {
- Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
- Ref<Texture2D> cb = close;
-
- w += get_theme_constant(SNAME("hseparation"));
-
- Rect2 cb_rect;
- cb_rect.size = style->get_minimum_size() + cb->get_size();
- if (rtl) {
- cb_rect.position.x = size.width - w - cb_rect.size.x;
- } else {
- cb_rect.position.x = w;
- }
- cb_rect.position.y = sb->get_margin(SIDE_TOP) + ((sb_rect.size.y - sb_ms.y) - (cb_rect.size.y)) / 2;
-
- if (!tabs[i].disabled && cb_hover == i) {
- if (cb_pressing) {
- get_theme_stylebox(SNAME("close_bg_pressed"))->draw(ci, cb_rect);
- } else {
- style->draw(ci, cb_rect);
- }
- }
+ ofs += tabs[i].size_cache;
+ }
- if (rtl) {
- cb->draw(ci, Point2i(size.width - w - cb_rect.size.x + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- } else {
- cb->draw(ci, Point2i(w + style->get_margin(SIDE_LEFT), cb_rect.position.y + style->get_margin(SIDE_TOP)));
- }
- w += cb->get_width();
- tabs.write[i].cb_rect = cb_rect;
- }
+ // Draw selected tab in the front, but only if it's visible.
+ if (current >= offset && current <= max_drawn_tab) {
+ 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;
- w += sb->get_margin(SIDE_RIGHT);
+ _draw_tab(sb, font_selected_color, current, x);
}
if (offset > 0 || missing_right) {
@@ -501,13 +379,98 @@ void TabBar::_notification(int p_what) {
draw_texture(incr, Point2(limit_minus_buttons + decr->get_size().width, vofs), Color(1, 1, 1, 0.5));
}
}
+ }
+ } break;
+ }
+}
+
+void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x) {
+ RID ci = get_canvas_item();
+
+ Color font_outline_color = get_theme_color(SNAME("font_outline_color"));
+ int outline_size = get_theme_constant(SNAME("outline_size"));
+
+ Tab tab = tabs[p_index];
- buttons_visible = true;
+ Rect2 sb_rect = Rect2(p_x, 0, tab.size_cache, get_size().height);
+ p_tab_style->draw(ci, sb_rect);
+
+ p_x += p_tab_style->get_margin(SIDE_LEFT);
+
+ Size2i sb_ms = p_tab_style->get_minimum_size();
+
+ Ref<Texture2D> icon = tab.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));
+
+ if (!tab.text.is_empty()) {
+ p_x += icon->get_width() + get_theme_constant(SNAME("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);
+
+ p_x += tab.size_text;
+
+ if (tab.right_button.is_valid()) {
+ Ref<StyleBox> style = get_theme_stylebox(SNAME("close_bg_highlight"));
+ Ref<Texture2D> rb = tab.right_button;
+
+ p_x += get_theme_constant(SNAME("hseparation"));
+
+ Rect2 rb_rect;
+ rb_rect.size = style->get_minimum_size() + rb->get_size();
+ rb_rect.position.x = 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;
+
+ if (rb_hover == p_index) {
+ if (rb_pressing) {
+ get_theme_stylebox(SNAME("button_pressed"))->draw(ci, rb_rect);
} else {
- buttons_visible = false;
+ style->draw(ci, rb_rect);
}
- } break;
+ }
+
+ 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;
}
+
+ 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<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.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) {
+ if (cb_pressing) {
+ get_theme_stylebox(SNAME("close_bg_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;
+ }
+}
+
+void TabBar::set_tab_count(int p_count) {
+ ERR_FAIL_COND(p_count < 0);
+ tabs.resize(p_count);
+ _update_cache();
+ update();
+ notify_property_list_changed();
}
int TabBar::get_tab_count() const {
@@ -553,8 +516,9 @@ 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();
update();
- minimum_size_changed();
+ update_minimum_size();
}
String TabBar::get_tab_title(int p_tab) const {
@@ -581,6 +545,7 @@ 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();
update();
}
@@ -590,6 +555,7 @@ void TabBar::set_tab_opentype_feature(int p_tab, const String &p_name, int p_val
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();
update();
}
}
@@ -620,8 +586,9 @@ 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();
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TabBar::get_tab_icon(int p_tab) const {
@@ -635,7 +602,7 @@ void TabBar::set_tab_disabled(int p_tab, bool p_disabled) {
update();
}
-bool TabBar::get_tab_disabled(int p_tab) const {
+bool TabBar::is_tab_disabled(int p_tab) const {
ERR_FAIL_INDEX_V(p_tab, tabs.size(), false);
return tabs[p_tab].disabled;
}
@@ -645,7 +612,7 @@ void TabBar::set_tab_right_button(int p_tab, const Ref<Texture2D> &p_right_butto
tabs.write[p_tab].right_button = p_right_button;
_update_cache();
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TabBar::get_tab_right_button(int p_tab) const {
@@ -659,7 +626,7 @@ void TabBar::_update_hover() {
}
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++) {
@@ -684,40 +651,50 @@ void TabBar::_update_hover() {
emit_signal(SNAME("tab_hovered"), hover);
}
- if (hover_buttons == -1) { // no hover
+ if (hover_buttons == -1) { // No hover.
rb_hover = hover_buttons;
cb_hover = hover_buttons;
}
}
void TabBar::_update_cache() {
+ if (tabs.is_empty()) {
+ return;
+ }
+
Ref<StyleBox> tab_disabled = get_theme_stylebox(SNAME("tab_disabled"));
Ref<StyleBox> tab_unselected = get_theme_stylebox(SNAME("tab_unselected"));
Ref<StyleBox> tab_selected = get_theme_stylebox(SNAME("tab_selected"));
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
- int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+
+ int limit = get_size().width;
+ 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;
+
for (int i = 0; i < tabs.size(); i++) {
- tabs.write[i].ofs_cache = mw;
+ 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++;
}
}
+
int m_width = min_width;
if (count_resize > 0) {
m_width = MAX((limit_minus_buttons - size_fixed) / count_resize, min_width);
}
+
for (int i = offset; i < tabs.size(); i++) {
Ref<StyleBox> sb;
if (tabs[i].disabled) {
@@ -727,11 +704,13 @@ void TabBar::_update_cache() {
} else {
sb = tab_unselected;
}
+
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"));
@@ -741,15 +720,52 @@ void TabBar::_update_cache() {
slen -= cb->get_width();
slen -= get_theme_constant(SNAME("hseparation"));
}
+
slen = MAX(slen, 1);
lsize = m_width;
}
}
+
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;
+
+ // 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;
+
+ while (w > limit_minus_buttons && max_drawn_tab > offset) {
+ w -= get_tab_width(max_drawn_tab);
+ max_drawn_tab -= 1;
+ }
+
+ break;
+ }
+ }
+
+ missing_right = max_drawn_tab < tabs.size() - 1;
+ buttons_visible = offset > 0 || missing_right;
+
+ if (tab_alignment == ALIGNMENT_LEFT) {
+ return;
+ } else 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;
}
}
@@ -765,19 +781,15 @@ 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.instantiate();
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;
- t.disabled = false;
- t.ofs_cache = 0;
- t.size_cache = 0;
tabs.push_back(t);
_update_cache();
call_deferred(SNAME("_update_hover"));
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TabBar::clear_tabs() {
@@ -786,6 +798,7 @@ void TabBar::clear_tabs() {
previous = 0;
call_deferred(SNAME("_update_hover"));
update();
+ notify_property_list_changed();
}
void TabBar::remove_tab(int p_idx) {
@@ -797,7 +810,7 @@ void TabBar::remove_tab(int p_idx) {
_update_cache();
call_deferred(SNAME("_update_hover"));
update();
- minimum_size_changed();
+ update_minimum_size();
if (current < 0) {
current = 0;
@@ -808,6 +821,7 @@ void TabBar::remove_tab(int p_idx) {
}
_ensure_no_over_offset();
+ notify_property_list_changed();
}
Variant TabBar::get_drag_data(const Point2 &p_point) {
@@ -860,7 +874,7 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
if (from_path == to_path) {
return true;
} else if (get_tabs_rearrange_group() != -1) {
- // drag and drop between other TabBars
+ // Drag and drop between other TabBars.
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()) {
@@ -895,7 +909,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
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
+ // 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()) {
@@ -911,10 +925,10 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) {
set_current_tab(hover_now);
emit_signal(SNAME("tab_changed"), hover_now);
_update_cache();
+ update();
}
}
}
- update();
}
int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
@@ -929,14 +943,15 @@ int TabBar::get_tab_idx_at_point(const Point2 &p_point) const {
return hover_now;
}
-void TabBar::set_tab_align(TabAlign p_align) {
- ERR_FAIL_INDEX(p_align, ALIGN_MAX);
- tab_align = p_align;
+void TabBar::set_tab_alignment(AlignmentMode p_alignment) {
+ ERR_FAIL_INDEX(p_alignment, ALIGNMENT_MAX);
+ tab_alignment = p_alignment;
+ _update_cache();
update();
}
-TabBar::TabAlign TabBar::get_tab_align() const {
- return tab_align;
+TabBar::AlignmentMode TabBar::get_tab_alignment() const {
+ return tab_alignment;
}
void TabBar::set_clip_tabs(bool p_clip_tabs) {
@@ -944,8 +959,9 @@ void TabBar::set_clip_tabs(bool p_clip_tabs) {
return;
}
clip_tabs = p_clip_tabs;
+ _update_cache();
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TabBar::get_clip_tabs() const {
@@ -966,6 +982,7 @@ void TabBar::move_tab(int from, int to) {
_update_cache();
update();
+ notify_property_list_changed();
}
int TabBar::get_tab_width(int p_idx) const {
@@ -980,7 +997,7 @@ int TabBar::get_tab_width(int p_idx) const {
Ref<Texture2D> tex = tabs[p_idx].icon;
if (tex.is_valid()) {
x += tex->get_width();
- if (tabs[p_idx].text != "") {
+ if (!tabs[p_idx].text.is_empty()) {
x += get_theme_constant(SNAME("hseparation"));
}
}
@@ -1011,64 +1028,73 @@ int TabBar::get_tab_width(int p_idx) const {
}
void TabBar::_ensure_no_over_offset() {
- if (!is_inside_tree()) {
+ if (!is_inside_tree() || !buttons_visible) {
return;
}
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
-
- int limit = get_size().width;
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
+ 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) {
- int total_w = 0;
- for (int i = offset - 1; i < tabs.size(); i++) {
- total_w += tabs[i].size_cache;
- }
+ total_w += tabs[offset - 1].size_cache;
- if ((buttons_visible && total_w < limit_minus_buttons) || total_w < limit) { // For the last tab, we accept if the tab covers the buttons.
+ if (total_w < limit_minus_buttons) {
offset--;
- update();
} else {
break;
}
}
-}
-void TabBar::ensure_tab_visible(int p_idx) {
- if (!is_inside_tree()) {
- return;
+ if (prev_offset != offset) {
+ _update_cache();
+ update();
}
+}
- if (tabs.size() == 0) {
+void TabBar::ensure_tab_visible(int p_idx) {
+ if (!is_inside_tree() || !buttons_visible) {
return;
}
ERR_FAIL_INDEX(p_idx, tabs.size());
- if (p_idx == offset) {
+ if (p_idx >= offset && p_idx <= max_drawn_tab) {
return;
}
+
if (p_idx < offset) {
offset = p_idx;
+ _update_cache();
update();
+
return;
}
- int prev_offset = offset;
Ref<Texture2D> incr = get_theme_icon(SNAME("increment"));
Ref<Texture2D> decr = get_theme_icon(SNAME("decrement"));
- int limit = get_size().width;
int limit_minus_buttons = get_size().width - incr->get_width() - decr->get_width();
- for (int i = offset; i <= p_idx; i++) {
- int total_w = tabs[i].ofs_cache + tabs[i].size_cache;
- if (total_w > limit || (buttons_visible && total_w > limit_minus_buttons)) {
+ int total_w = tabs[max_drawn_tab].ofs_cache - tabs[offset].ofs_cache;
+ for (int i = max_drawn_tab; i <= p_idx; i++) {
+ total_w += tabs[i].size_cache;
+ }
+
+ int prev_offset = offset;
+
+ for (int i = offset; i < p_idx; i++) {
+ if (total_w > limit_minus_buttons) {
+ total_w -= tabs[i].size_cache;
offset++;
+ } else {
+ break;
}
}
if (prev_offset != offset) {
+ _update_cache();
update();
}
}
@@ -1085,6 +1111,7 @@ 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();
update();
}
@@ -1128,8 +1155,61 @@ bool TabBar::get_select_with_rmb() const {
return select_with_rmb;
}
+bool TabBar::_set(const StringName &p_name, const Variant &p_value) {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) {
+ int tab_index = components[0].trim_prefix("tab_").to_int();
+ String property = components[1];
+ if (property == "title") {
+ set_tab_title(tab_index, p_value);
+ return true;
+ } else if (property == "icon") {
+ set_tab_icon(tab_index, p_value);
+ return true;
+ } else if (components[1] == "disabled") {
+ set_tab_disabled(tab_index, p_value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool TabBar::_get(const StringName &p_name, Variant &r_ret) const {
+ Vector<String> components = String(p_name).split("/", true, 2);
+ if (components.size() >= 2 && components[0].begins_with("tab_") && components[0].trim_prefix("tab_").is_valid_int()) {
+ int tab_index = components[0].trim_prefix("tab_").to_int();
+ String property = components[1];
+ if (property == "title") {
+ r_ret = get_tab_title(tab_index);
+ return true;
+ } else if (property == "icon") {
+ r_ret = get_tab_icon(tab_index);
+ return true;
+ } else if (components[1] == "disabled") {
+ r_ret = is_tab_disabled(tab_index);
+ return true;
+ }
+ }
+ return false;
+}
+
+void TabBar::_get_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < tabs.size(); i++) {
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("tab_%d/title", i)));
+
+ PropertyInfo pi = PropertyInfo(Variant::OBJECT, vformat("tab_%d/icon", i), PROPERTY_HINT_RESOURCE_TYPE, "Texture2D");
+ pi.usage &= ~(get_tab_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+
+ pi = PropertyInfo(Variant::BOOL, vformat("tab_%d/disabled", i));
+ pi.usage &= ~(!is_tab_disabled(i) ? PROPERTY_USAGE_STORAGE : 0);
+ p_list->push_back(pi);
+ }
+}
+
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);
ClassDB::bind_method(D_METHOD("get_current_tab"), &TabBar::get_current_tab);
@@ -1146,11 +1226,11 @@ void TabBar::_bind_methods() {
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_disabled", "tab_idx", "disabled"), &TabBar::set_tab_disabled);
- ClassDB::bind_method(D_METHOD("get_tab_disabled", "tab_idx"), &TabBar::get_tab_disabled);
+ ClassDB::bind_method(D_METHOD("is_tab_disabled", "tab_idx"), &TabBar::is_tab_disabled);
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("set_tab_align", "align"), &TabBar::set_tab_align);
- ClassDB::bind_method(D_METHOD("get_tab_align"), &TabBar::get_tab_align);
+ 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);
ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabBar::get_clip_tabs);
ClassDB::bind_method(D_METHOD("get_tab_offset"), &TabBar::get_tab_offset);
@@ -1178,16 +1258,18 @@ void TabBar::_bind_methods() {
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_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
+ 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::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");
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
- BIND_ENUM_CONSTANT(ALIGN_MAX);
+ ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_");
+
+ BIND_ENUM_CONSTANT(ALIGNMENT_LEFT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_MAX);
BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_NEVER);
BIND_ENUM_CONSTANT(CLOSE_BUTTON_SHOW_ACTIVE_ONLY);
diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h
index 411a62b1d9..d0055ae4d2 100644
--- a/scene/gui/tab_bar.h
+++ b/scene/gui/tab_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,11 +38,11 @@ class TabBar : public Control {
GDCLASS(TabBar, Control);
public:
- enum TabAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT,
- ALIGN_MAX
+ enum AlignmentMode {
+ ALIGNMENT_LEFT,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_RIGHT,
+ ALIGNMENT_MAX,
};
enum CloseButtonDisplayPolicy {
@@ -73,6 +73,10 @@ private:
Ref<Texture2D> right_button;
Rect2 rb_rect;
Rect2 cb_rect;
+
+ Tab() {
+ text_buf.instantiate();
+ }
};
int offset = 0;
@@ -83,7 +87,7 @@ private:
Vector<Tab> tabs;
int current = 0;
int previous = 0;
- TabAlign tab_align = ALIGN_CENTER;
+ AlignmentMode tab_alignment = ALIGNMENT_CENTER;
bool clip_tabs = true;
int rb_hover = -1;
bool rb_pressing = false;
@@ -109,9 +113,13 @@ private:
void _on_mouse_exited();
void _shape(int p_tab);
+ void _draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_index, float p_x);
protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
+ bool _set(const StringName &p_name, const Variant &p_value);
+ bool _get(const StringName &p_name, Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
void _notification(int p_what);
static void _bind_methods();
@@ -140,13 +148,13 @@ 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_right_button(int p_tab, const Ref<Texture2D> &p_right_button);
Ref<Texture2D> get_tab_right_button(int p_tab) const;
- void set_tab_align(TabAlign p_align);
- TabAlign get_tab_align() const;
+ 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;
@@ -156,7 +164,9 @@ public:
void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy);
CloseButtonDisplayPolicy get_tab_close_display_policy() const;
+ void set_tab_count(int p_count);
int get_tab_count() const;
+
void set_current_tab(int p_current);
int get_current_tab() const;
int get_previous_tab() const;
@@ -189,7 +199,7 @@ public:
TabBar();
};
-VARIANT_ENUM_CAST(TabBar::TabAlign);
+VARIANT_ENUM_CAST(TabBar::AlignmentMode);
VARIANT_ENUM_CAST(TabBar::CloseButtonDisplayPolicy);
#endif // TAB_BAR_H
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index ff53d91ea3..c3fc08731e 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -406,14 +406,14 @@ void TabContainer::_notification(int p_what) {
}
// Find the offset at which to draw tabs, according to the alignment.
- switch (align) {
- case ALIGN_LEFT:
+ switch (alignment) {
+ case ALIGNMENT_LEFT:
tabs_ofs_cache = header_x;
break;
- case ALIGN_CENTER:
+ case ALIGNMENT_CENTER:
tabs_ofs_cache = header_x + (header_width / 2) - (all_tabs_width / 2);
break;
- case ALIGN_RIGHT:
+ case ALIGNMENT_RIGHT:
tabs_ofs_cache = header_x + header_width - all_tabs_width;
break;
}
@@ -561,7 +561,7 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in
if (icon.is_valid()) {
int y = y_center - (icon->get_height() / 2);
icon->draw(canvas, Point2i(x_content, y));
- if (text != "") {
+ if (!text.is_empty()) {
x_content += icon->get_width() + icon_text_distance;
}
}
@@ -600,7 +600,7 @@ void TabContainer::_on_theme_changed() {
_refresh_texts();
- minimum_size_changed();
+ update_minimum_size();
if (get_tab_count() > 0) {
_repaint();
update();
@@ -656,7 +656,7 @@ int TabContainer::_get_tab_width(int p_index) const {
Ref<Texture2D> icon = control->get_meta("_tab_icon");
if (icon.is_valid()) {
width += icon->get_width();
- if (text != "") {
+ if (!text.is_empty()) {
width += get_theme_constant(SNAME("icon_separation"));
}
}
@@ -703,30 +703,16 @@ void TabContainer::add_child_notify(Node *p_child) {
return;
}
- Vector<Control *> tabs = _get_tabs();
_refresh_texts();
+ call_deferred("_repaint");
+ update();
- bool first = false;
-
- if (tabs.size() != 1) {
- c->hide();
- } else {
- c->show();
- //call_deferred(SNAME("set_current_tab"),0);
- first = true;
+ bool first = (_get_tabs().size() == 1);
+ if (first) {
current = 0;
previous = 0;
}
- c->set_anchors_and_offsets_preset(Control::PRESET_WIDE);
- if (tabs_visible) {
- c->set_offset(SIDE_TOP, _get_top_margin());
- }
- Ref<StyleBox> sb = get_theme_stylebox(SNAME("panel"));
- 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));
- update();
+
p_child->connect("renamed", callable_mp(this, &TabContainer::_child_renamed_callback));
if (first && is_inside_tree()) {
emit_signal(SNAME("tab_changed"), current);
@@ -967,14 +953,14 @@ int TabContainer::get_tab_idx_at_point(const Point2 &p_point) const {
return -1;
}
-void TabContainer::set_tab_align(TabAlign p_align) {
- ERR_FAIL_INDEX(p_align, 3);
- align = p_align;
+void TabContainer::set_tab_alignment(AlignmentMode p_alignment) {
+ ERR_FAIL_INDEX(p_alignment, 3);
+ alignment = p_alignment;
update();
}
-TabContainer::TabAlign TabContainer::get_tab_align() const {
- return align;
+TabContainer::AlignmentMode TabContainer::get_tab_alignment() const {
+ return alignment;
}
void TabContainer::set_tabs_visible(bool p_visible) {
@@ -995,7 +981,7 @@ void TabContainer::set_tabs_visible(bool p_visible) {
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TabContainer::are_tabs_visible() const {
@@ -1108,7 +1094,7 @@ void TabContainer::get_translatable_strings(List<String> *p_strings) const {
String name = c->get_meta("_tab_name");
- if (name != "") {
+ if (!name.is_empty()) {
p_strings->push_back(name);
}
}
@@ -1198,8 +1184,8 @@ void TabContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_previous_tab"), &TabContainer::get_previous_tab);
ClassDB::bind_method(D_METHOD("get_current_tab_control"), &TabContainer::get_current_tab_control);
ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control);
- ClassDB::bind_method(D_METHOD("set_tab_align", "align"), &TabContainer::set_tab_align);
- ClassDB::bind_method(D_METHOD("get_tab_align"), &TabContainer::get_tab_align);
+ 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_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);
@@ -1223,6 +1209,7 @@ void TabContainer::_bind_methods() {
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);
@@ -1230,16 +1217,16 @@ void TabContainer::_bind_methods() {
ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab")));
ADD_SIGNAL(MethodInfo("pre_popup_pressed"));
- ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_align", "get_tab_align");
+ 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, "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::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size");
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_LEFT);
+ BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
+ BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT);
}
TabContainer::TabContainer() {
diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h
index fe96df25e8..01e71e9fa8 100644
--- a/scene/gui/tab_container.h
+++ b/scene/gui/tab_container.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,10 +39,10 @@ class TabContainer : public Container {
GDCLASS(TabContainer, Container);
public:
- enum TabAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
+ enum AlignmentMode {
+ ALIGNMENT_LEFT,
+ ALIGNMENT_CENTER,
+ ALIGNMENT_RIGHT,
};
private:
@@ -56,7 +56,7 @@ private:
bool buttons_visible_cache = false;
bool menu_hovered = false;
int highlight_arrow = -1;
- TabAlign align = ALIGN_CENTER;
+ AlignmentMode alignment = ALIGNMENT_CENTER;
int _get_top_margin() const;
mutable ObjectID popup_obj_id;
bool drag_to_rearrange_enabled = false;
@@ -90,8 +90,8 @@ protected:
static void _bind_methods();
public:
- void set_tab_align(TabAlign p_align);
- TabAlign get_tab_align() const;
+ void set_tab_alignment(AlignmentMode p_alignment);
+ AlignmentMode get_tab_alignment() const;
void set_tabs_visible(bool p_visible);
bool are_tabs_visible() const;
@@ -136,6 +136,6 @@ public:
TabContainer();
};
-VARIANT_ENUM_CAST(TabContainer::TabAlign);
+VARIANT_ENUM_CAST(TabContainer::AlignmentMode);
#endif // TAB_CONTAINER_H
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 2cb9d10fca..d6b449abd1 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -39,6 +39,7 @@
#include "core/os/os.h"
#include "core/string/string_builder.h"
#include "core/string/translation.h"
+#include "label.h"
#include "scene/main/window.h"
@@ -154,30 +155,30 @@ _FORCE_INLINE_ const String &TextEdit::Text::operator[](int p_line) const {
void TextEdit::Text::_calculate_line_height() {
int height = 0;
- for (int i = 0; i < text.size(); i++) {
+ for (const Line &l : text) {
// Found another line with the same height...nothing to update.
- if (text[i].height == line_height) {
+ if (l.height == line_height) {
height = line_height;
break;
}
- height = MAX(height, text[i].height);
+ height = MAX(height, l.height);
}
line_height = height;
}
void TextEdit::Text::_calculate_max_line_width() {
int width = 0;
- for (int i = 0; i < text.size(); i++) {
- if (is_hidden(i)) {
+ for (const Line &l : text) {
+ if (l.hidden) {
continue;
}
// Found another line with the same width...nothing to update.
- if (text[i].width == max_width) {
+ if (l.width == max_width) {
width = max_width;
break;
}
- width = MAX(width, text[i].width);
+ width = MAX(width, l.width);
}
max_width = width;
}
@@ -215,7 +216,7 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, const String &p_
// Update height.
const int old_height = text.write[p_line].height;
const int wrap_amount = get_line_wrap_amount(p_line);
- int height = font->get_height(font_size);
+ int height = font_height;
for (int i = 0; i <= wrap_amount; i++) {
height = MAX(height, text[p_line].data_buf->get_line_size(i).y);
}
@@ -266,6 +267,13 @@ void TextEdit::Text::invalidate_all() {
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);
}
@@ -274,7 +282,15 @@ void TextEdit::Text::invalidate_all() {
void TextEdit::Text::clear() {
text.clear();
- insert(0, "", Array());
+
+ max_width = -1;
+ line_height = -1;
+
+ Line line;
+ line.gutters.resize(gutter_count);
+ line.data = "";
+ text.insert(0, line);
+ invalidate_cache(0);
}
int TextEdit::Text::get_max_width() const {
@@ -289,30 +305,64 @@ void TextEdit::Text::set(int p_line, const String &p_text, const Array &p_bidi_o
invalidate_cache(p_line);
}
-void TextEdit::Text::insert(int p_at, const String &p_text, const Array &p_bidi_override) {
- Line line;
- line.gutters.resize(gutter_count);
- line.hidden = false;
- line.data = p_text;
- line.bidi_override = p_bidi_override;
- text.insert(p_at, line);
+void TextEdit::Text::insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override) {
+ int new_line_count = p_text.size() - 1;
+ if (new_line_count > 0) {
+ text.resize(text.size() + new_line_count);
+ for (int i = (text.size() - 1); i > p_at; i--) {
+ if ((i - new_line_count) <= 0) {
+ break;
+ }
+ text.write[i] = text[i - new_line_count];
+ }
+ }
- invalidate_cache(p_at);
+ for (int i = 0; i < p_text.size(); i++) {
+ if (i == 0) {
+ set(p_at + i, p_text[i], p_bidi_override[i]);
+ continue;
+ }
+ Line line;
+ line.gutters.resize(gutter_count);
+ line.data = p_text[i];
+ line.bidi_override = p_bidi_override[i];
+ text.write[p_at + i] = line;
+ invalidate_cache(p_at + i);
+ }
}
-void TextEdit::Text::remove_at(int p_index) {
- int height = text[p_index].height;
- int width = text[p_index].width;
+void TextEdit::Text::remove_range(int p_from_line, int p_to_line) {
+ if (p_from_line == p_to_line) {
+ return;
+ }
+
+ bool dirty_height = false;
+ bool dirty_width = false;
+ for (int i = p_from_line; i < p_to_line; i++) {
+ if (!dirty_height && text[i].height == line_height) {
+ dirty_height = true;
+ }
+
+ if (!dirty_width && text[i].width == max_width) {
+ dirty_width = true;
+ }
+
+ if (dirty_height && dirty_width) {
+ break;
+ }
+ }
- text.remove_at(p_index);
+ int diff = (p_to_line - p_from_line);
+ for (int i = p_to_line; i < text.size() - 1; i++) {
+ text.write[(i - diff) + 1] = text[i + 1];
+ }
+ text.resize(text.size() - diff);
- // If this is the tallest line, we need to get the next tallest.
- if (height == line_height) {
+ if (dirty_height) {
_calculate_line_height();
}
- // If this is the longest line, we need to get the next longest.
- if (width == max_width) {
+ if (dirty_width) {
_calculate_max_line_width();
}
}
@@ -600,7 +650,7 @@ void TextEdit::_notification(int p_what) {
String highlighted_text = get_selected_text();
// Check if highlighted words contain only whitespaces (tabs or spaces).
- bool only_whitespaces_highlighted = highlighted_text.strip_edges() == String();
+ bool only_whitespaces_highlighted = highlighted_text.strip_edges().is_empty();
const int caret_wrap_index = get_caret_wrap_index();
@@ -915,7 +965,7 @@ void TextEdit::_notification(int p_what) {
switch (gutter.type) {
case GUTTER_TYPE_STRING: {
const String &text = get_line_gutter_text(line, g);
- if (text == "") {
+ if (text.is_empty()) {
break;
}
@@ -958,15 +1008,17 @@ void TextEdit::_notification(int p_what) {
icon->draw_rect(ci, gutter_rect, false, get_line_gutter_item_color(line, g));
} break;
case GUTTER_TYPE_CUSTOM: {
- if (gutter.custom_draw_obj.is_valid()) {
- Object *cdo = ObjectDB::get_instance(gutter.custom_draw_obj);
- if (cdo) {
- Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
- if (rtl) {
- gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
- }
- cdo->call(gutter.custom_draw_callback, line, g, Rect2(gutter_rect));
+ if (gutter.custom_draw_callback.is_valid()) {
+ Rect2i gutter_rect = Rect2i(Point2i(gutter_offset, ofs_y), Size2i(gutter.width, row_height));
+ if (rtl) {
+ gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x;
}
+
+ Variant args[3] = { line, g, Rect2(gutter_rect) };
+ const Variant *argp[] = { &args[0], &args[1], &args[2] };
+ Callable::CallError ce;
+ Variant ret;
+ gutter.custom_draw_callback.call(argp, 3, ret, ce);
}
} break;
}
@@ -1055,7 +1107,7 @@ void TextEdit::_notification(int p_what) {
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);
for (int j = 0; j < sel.size(); j++) {
- Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y, sel[j].y - sel[j].x, row_height);
+ Rect2 rect = Rect2(sel[j].x + char_margin + ofs_x, ofs_y + (line_spacing / 2), sel[j].y - sel[j].x, row_height);
if (rect.position.x + rect.size.x <= xmargin_beg || rect.position.x > xmargin_end) {
continue;
}
@@ -1065,9 +1117,9 @@ void TextEdit::_notification(int p_what) {
} else if (rect.position.x + rect.size.x > xmargin_end) {
rect.size.x = xmargin_end - rect.position.x;
}
- rect.position.y = TS->shaped_text_get_ascent(rid) + font->get_underline_position(font_size);
- rect.size.y = font->get_underline_thickness(font_size);
- draw_rect(rect, font_selected_color);
+ rect.position.y += ceil(TS->shaped_text_get_ascent(rid)) + ceil(font->get_underline_position(font_size));
+ rect.size.y = MAX(1, font->get_underline_thickness(font_size));
+ draw_rect(rect, color);
}
highlighted_word_col = _get_column_pos_of_word(lookup_symbol_word, str, SEARCH_MATCH_CASE | SEARCH_WHOLE_WORDS, highlighted_word_col + 1);
@@ -1085,7 +1137,7 @@ void TextEdit::_notification(int p_what) {
int first_visible_char = TS->shaped_text_get_range(rid).y;
int last_visible_char = TS->shaped_text_get_range(rid).x;
- int char_ofs = 0;
+ float char_ofs = 0;
if (outline_size > 0 && outline_color.a > 0) {
for (int j = 0; j < gl_size; j++) {
for (int k = 0; k < glyphs[j].repeat; k++) {
@@ -1120,7 +1172,7 @@ void TextEdit::_notification(int p_what) {
}
}
- int char_pos = char_ofs + char_margin + ofs_x;
+ float char_pos = char_ofs + char_margin + ofs_x;
if (char_pos >= xmargin_beg) {
if (highlight_matching_braces_enabled) {
if ((brace_open_match_line == line && brace_open_match_column == glyphs[j].start) ||
@@ -1194,7 +1246,7 @@ void TextEdit::_notification(int p_what) {
}
// Carets.
- int caret_width = Math::round(1 * get_theme_default_base_scale());
+ const int caret_width = get_theme_constant(SNAME("caret_width")) * 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);
@@ -1224,7 +1276,7 @@ void TextEdit::_notification(int p_what) {
if (caret.draw_pos.x >= xmargin_beg && caret.draw_pos.x < xmargin_end) {
caret.visible = true;
- if (draw_caret) {
+ if (draw_caret || drag_caret_force_displayed) {
if (caret_type == CaretType::CARET_TYPE_BLOCK || overtype_mode) {
//Block or underline caret, draw trailing carets at full height.
int h = font->get_height(font_size);
@@ -1391,7 +1443,7 @@ void TextEdit::_notification(int p_what) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
- if (deselect_on_focus_loss_enabled) {
+ if (deselect_on_focus_loss_enabled && !selection.drag_attempt) {
deselect();
}
} break;
@@ -1411,6 +1463,30 @@ void TextEdit::_notification(int p_what) {
update();
}
} break;
+ case Control::NOTIFICATION_DRAG_BEGIN: {
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
+ drag_action = true;
+ dragging_minimap = false;
+ dragging_selection = false;
+ can_drag_minimap = false;
+ click_select_held->stop();
+ } break;
+ case Control::NOTIFICATION_DRAG_END: {
+ if (is_drag_successful()) {
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (is_editable() && !Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ delete_selection();
+ } else if (deselect_on_focus_loss_enabled) {
+ deselect();
+ }
+ }
+ } else {
+ selection.drag_attempt = false;
+ }
+ drag_action = false;
+ drag_caret_force_displayed = false;
+ } break;
}
}
@@ -1495,6 +1571,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
set_caret_line(row, false, false);
set_caret_column(col);
+ selection.drag_attempt = false;
if (mb->is_shift_pressed() && (caret.column != prev_col || caret.line != prev_line)) {
if (!selection.active) {
@@ -1538,6 +1615,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
update();
}
+ } else if (is_mouse_over_selection()) {
+ selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE;
+ selection.drag_attempt = true;
} else {
selection.active = false;
selection.selecting_mode = SelectionMode::SELECTION_MODE_POINTER;
@@ -1551,6 +1631,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (!mb->is_double_click() && (OS::get_singleton()->get_ticks_msec() - last_dblclk) < triple_click_timeout && mb->get_position().distance_to(last_dblclk_pos) < triple_click_tolerance) {
// Triple-click select line.
selection.selecting_mode = SelectionMode::SELECTION_MODE_LINE;
+ selection.drag_attempt = false;
_update_selection_mode_line();
last_dblclk = 0;
} else if (mb->is_double_click() && text[caret.line].length()) {
@@ -1594,17 +1675,23 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
}
_generate_context_menu();
- menu->set_position(get_screen_transform().xform(mpos));
+ menu->set_position(get_screen_position() + mpos);
menu->reset_size();
menu->popup();
grab_focus();
}
} else {
if (mb->get_button_index() == MouseButton::LEFT) {
+ if (selection.drag_attempt && is_mouse_over_selection()) {
+ selection.active = false;
+ }
dragging_minimap = false;
dragging_selection = false;
can_drag_minimap = false;
click_select_held->stop();
+ if (!drag_action) {
+ selection.drag_attempt = false;
+ }
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CLIPBOARD_PRIMARY)) {
DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text());
}
@@ -1689,6 +1776,14 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
hovered_gutter = current_hovered_gutter;
update();
}
+
+ if (drag_action && can_drop_data(mpos, get_viewport()->gui_get_drag_data())) {
+ drag_caret_force_displayed = true;
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ set_caret_line(pos.y, false);
+ set_caret_column(pos.x);
+ dragging_selection = true;
+ }
}
if (draw_minimap && !dragging_selection) {
@@ -1827,7 +1922,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) {
if (context_menu_enabled) {
_generate_context_menu();
adjust_viewport_to_caret();
- menu->set_position(get_screen_transform().xform(get_caret_draw_pos()));
+ menu->set_position(get_screen_position() + get_caret_draw_pos());
menu->reset_size();
menu->popup();
menu->grab_focus();
@@ -2384,7 +2479,7 @@ void TextEdit::_update_caches() {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.set_font_features(opentype_features);
text.set_draw_control_chars(draw_control_chars);
text.set_font(font);
@@ -2406,6 +2501,75 @@ bool TextEdit::is_text_field() const {
return true;
}
+Variant TextEdit::get_drag_data(const Point2 &p_point) {
+ if (selection.active && selection.drag_attempt) {
+ String t = get_selected_text();
+ Label *l = memnew(Label);
+ l->set_text(t);
+ set_drag_preview(l);
+ return t;
+ }
+
+ return Variant();
+}
+
+bool TextEdit::can_drop_data(const Point2 &p_point, const Variant &p_data) const {
+ bool drop_override = Control::can_drop_data(p_point, p_data); // In case user wants to drop custom data.
+ if (drop_override) {
+ return drop_override;
+ }
+
+ return is_editable() && p_data.get_type() == Variant::STRING;
+}
+
+void TextEdit::drop_data(const Point2 &p_point, const Variant &p_data) {
+ Control::drop_data(p_point, p_data);
+
+ if (p_data.get_type() == Variant::STRING && is_editable()) {
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ int caret_row_tmp = pos.y;
+ int caret_column_tmp = pos.x;
+ if (selection.drag_attempt) {
+ selection.drag_attempt = false;
+ if (!is_mouse_over_selection(!Input::get_singleton()->is_key_pressed(Key::CTRL))) {
+ begin_complex_operation();
+ if (!Input::get_singleton()->is_key_pressed(Key::CTRL)) {
+ if (caret_row_tmp > selection.to_line) {
+ caret_row_tmp = caret_row_tmp - (selection.to_line - selection.from_line);
+ } else if (caret_row_tmp == selection.to_line && caret_column_tmp >= selection.to_column) {
+ caret_column_tmp = caret_column_tmp - (selection.to_column - selection.from_column);
+ }
+ delete_selection();
+ } else {
+ deselect();
+ }
+
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ end_complex_operation();
+ }
+ } else if (is_mouse_over_selection()) {
+ caret_row_tmp = selection.from_line;
+ caret_column_tmp = selection.from_column;
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ grab_focus();
+ } else {
+ deselect();
+ set_caret_line(caret_row_tmp, true, false);
+ set_caret_column(caret_column_tmp);
+ insert_text_at_caret(p_data);
+ grab_focus();
+ }
+
+ if (caret_row_tmp != caret.line || caret_column_tmp != caret.column) {
+ select(caret_row_tmp, caret_column_tmp, caret.line, caret.column);
+ }
+ }
+}
+
Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
Point2i pos = get_line_column_at_pos(p_pos);
int row = pos.y;
@@ -2436,8 +2600,7 @@ Control::CursorShape TextEdit::get_cursor_shape(const Point2 &p_pos) const {
}
String TextEdit::get_tooltip(const Point2 &p_pos) const {
- Object *tooltip_obj = ObjectDB::get_instance(tooltip_obj_id);
- if (!tooltip_obj) {
+ if (!tooltip_callback.is_valid()) {
return Control::get_tooltip(p_pos);
}
Point2i pos = get_line_column_at_pos(p_pos);
@@ -2450,19 +2613,20 @@ String TextEdit::get_tooltip(const Point2 &p_pos) const {
}
int beg, end;
if (select_word(s, col, beg, end)) {
- String tt = tooltip_obj->call(tooltip_func, s.substr(beg, end - beg), tooltip_ud);
-
- return tt;
+ Variant args[1] = { s.substr(beg, end - beg) };
+ const Variant *argp[] = { &args[0] };
+ Callable::CallError ce;
+ Variant ret;
+ tooltip_callback.call(argp, 1, ret, ce);
+ ERR_FAIL_COND_V_MSG(ce.error != Callable::CallError::CALL_OK, "", "Failed to call custom tooltip.");
+ return ret;
}
return Control::get_tooltip(p_pos);
}
-void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata) {
- ERR_FAIL_NULL(p_obj);
- tooltip_obj_id = p_obj->get_instance_id();
- tooltip_func = p_function;
- tooltip_ud = p_udata;
+void TextEdit::set_tooltip_request_func(const Callable &p_tooltip_callback) {
+ tooltip_callback = p_tooltip_callback;
}
/* Text */
@@ -2498,7 +2662,7 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.invalidate_all();
if (menu_dir) {
@@ -2549,7 +2713,7 @@ void TextEdit::set_language(const String &p_language) {
} else {
dir = (TextServer::Direction)text_direction;
}
- text.set_direction_and_language(dir, (language != "") ? language : TranslationServer::get_singleton()->get_tool_locale());
+ text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.invalidate_all();
update();
}
@@ -2650,6 +2814,17 @@ void TextEdit::clear() {
}
void TextEdit::_clear() {
+ if (editable && undo_enabled) {
+ _move_caret_document_start(false);
+ begin_complex_operation();
+
+ _remove_text(0, 0, MAX(0, get_line_count() - 1), MAX(get_line(MAX(get_line_count() - 1, 0)).size() - 1, 0));
+ insert_text_at_caret("");
+ text.clear();
+
+ end_complex_operation();
+ return;
+ }
clear_undo_history();
text.clear();
caret.column = 0;
@@ -3580,6 +3755,21 @@ bool TextEdit::is_dragging_cursor() const {
return dragging_selection || dragging_minimap;
}
+bool TextEdit::is_mouse_over_selection(bool p_edges) const {
+ if (!has_selection()) {
+ return false;
+ }
+ Point2i pos = get_line_column_at_pos(get_local_mouse_pos());
+ int row = pos.y;
+ int col = pos.x;
+ if (p_edges) {
+ if ((row == selection.from_line && col == selection.from_column) || (row == selection.to_line && col == selection.to_column)) {
+ return true;
+ }
+ }
+ return (row >= selection.from_line && row <= selection.to_line && (row > selection.from_line || col > selection.from_column) && (row < selection.to_line || col < selection.to_column));
+}
+
/* Caret */
void TextEdit::set_caret_type(CaretType p_type) {
caret_type = p_type;
@@ -4098,7 +4288,10 @@ double TextEdit::get_scroll_pos_for_line(int p_line, int p_wrap_index) const {
return p_line;
}
- double new_line_scroll_pos = get_visible_line_count_in_range(0, CLAMP(p_line, 0, text.size() - 1));
+ double new_line_scroll_pos = 0.0;
+ if (p_line > 0) {
+ new_line_scroll_pos = get_visible_line_count_in_range(0, MIN(p_line - 1, text.size() - 1));
+ }
new_line_scroll_pos += p_wrap_index;
return new_line_scroll_pos;
}
@@ -4162,7 +4355,7 @@ int TextEdit::get_visible_line_count_in_range(int p_from_line, int p_to_line) co
/* Returns the total number of (lines + wrapped - hidden). */
if (!_is_hiding_enabled() && get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
- return p_to_line - p_from_line;
+ return (p_to_line - p_from_line) + 1;
}
int total_rows = 0;
@@ -4467,12 +4660,10 @@ void TextEdit::merge_gutters(int p_from_line, int p_to_line) {
update();
}
-void TextEdit::set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback) {
+void TextEdit::set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback) {
ERR_FAIL_INDEX(p_gutter, gutters.size());
- ERR_FAIL_NULL(p_object);
- gutters.write[p_gutter].custom_draw_obj = p_object->get_instance_id();
- gutters.write[p_gutter].custom_draw_callback = p_callback;
+ gutters.write[p_gutter].custom_draw_callback = p_draw_callback;
update();
}
@@ -4762,7 +4953,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("search", "text", "flags", "from_line", "from_colum"), &TextEdit::search);
/* Tooltip */
- ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "object", "callback", "data"), &TextEdit::set_tooltip_request_func);
+ ClassDB::bind_method(D_METHOD("set_tooltip_request_func", "callback"), &TextEdit::set_tooltip_request_func);
/* Mouse */
ClassDB::bind_method(D_METHOD("get_local_mouse_pos"), &TextEdit::get_local_mouse_pos);
@@ -4776,6 +4967,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_minimap_line_at_pos", "position"), &TextEdit::get_minimap_line_at_pos);
ClassDB::bind_method(D_METHOD("is_dragging_cursor"), &TextEdit::is_dragging_cursor);
+ ClassDB::bind_method(D_METHOD("is_mouse_over_selection", "edges"), &TextEdit::is_mouse_over_selection);
/* Caret. */
BIND_ENUM_CONSTANT(CARET_TYPE_LINE);
@@ -4867,7 +5059,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_line_wrapped_text", "line"), &TextEdit::get_line_wrapped_text);
/* Viewport. */
- // Scolling.
+ // Scrolling.
ClassDB::bind_method(D_METHOD("set_smooth_scroll_enable", "enable"), &TextEdit::set_smooth_scroll_enabled);
ClassDB::bind_method(D_METHOD("is_smooth_scroll_enabled"), &TextEdit::is_smooth_scroll_enabled);
@@ -4933,7 +5125,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_gutter_overwritable", "gutter", "overwritable"), &TextEdit::set_gutter_overwritable);
ClassDB::bind_method(D_METHOD("is_gutter_overwritable", "gutter"), &TextEdit::is_gutter_overwritable);
ClassDB::bind_method(D_METHOD("merge_gutters", "from_line", "to_line"), &TextEdit::merge_gutters);
- ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "object", "callback"), &TextEdit::set_gutter_custom_draw);
+ ClassDB::bind_method(D_METHOD("set_gutter_custom_draw", "column", "draw_callback"), &TextEdit::set_gutter_custom_draw);
ClassDB::bind_method(D_METHOD("get_total_gutter_width"), &TextEdit::get_total_gutter_width);
// Line gutters.
@@ -4979,7 +5171,7 @@ void TextEdit::_bind_methods() {
/* Inspector */
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
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"), "set_language", "get_language");
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled");
@@ -5047,7 +5239,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
if (str.begins_with("opentype_features/")) {
String name = str.get_slicec('/', 1);
int32_t tag = TS->name_to_tag(name);
- double value = p_value;
+ int value = p_value;
if (value == -1) {
if (opentype_features.has(tag)) {
opentype_features.erase(tag);
@@ -5056,7 +5248,7 @@ bool TextEdit::_set(const StringName &p_name, const Variant &p_value) {
update();
}
} else {
- if ((double)opentype_features[tag] != value) {
+ if (!opentype_features.has(tag) || (int)opentype_features[tag] != value) {
opentype_features[tag] = value;
text.set_font_features(opentype_features);
text.invalidate_all();
@@ -5089,7 +5281,7 @@ bool TextEdit::_get(const StringName &p_name, Variant &r_ret) const {
void TextEdit::_get_property_list(List<PropertyInfo> *p_list) const {
for (const Variant *ftr = opentype_features.next(nullptr); ftr != nullptr; ftr = opentype_features.next(ftr)) {
String name = TS->tag_to_name(*ftr);
- p_list->push_back(PropertyInfo(Variant::FLOAT, "opentype_features/" + name));
+ p_list->push_back(PropertyInfo(Variant::INT, "opentype_features/" + name));
}
p_list->push_back(PropertyInfo(Variant::NIL, "opentype_features/_new", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR));
}
@@ -5229,7 +5421,7 @@ void TextEdit::_cut_internal() {
set_caret_line(get_caret_line() + 1);
}
- // Correct the visualy perceived caret column taking care of identation level of the lines.
+ // Correct the visually perceived caret column taking care of indentation level of the lines.
int diff_indent = indent_level - get_indent_level(get_caret_line());
cc += diff_indent;
if (diff_indent != 0) {
@@ -5768,6 +5960,7 @@ void TextEdit::_update_scrollbars() {
caret.line_ofs = 0;
caret.wrap_ofs = 0;
v_scroll->set_value(0);
+ v_scroll->set_max(0);
v_scroll->hide();
}
@@ -5785,6 +5978,7 @@ void TextEdit::_update_scrollbars() {
} else {
caret.x_ofs = 0;
h_scroll->set_value(0);
+ h_scroll->set_max(0);
h_scroll->hide();
}
@@ -6160,11 +6354,11 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
ERR_FAIL_COND(p_char < 0);
/* STEP 1: Remove \r from source text and separate in substrings. */
-
- Vector<String> substrings = p_text.replace("\r", "").split("\n");
+ const String text_to_insert = p_text.replace("\r", "");
+ Vector<String> substrings = text_to_insert.split("\n");
// Is this just a new empty line?
- bool shift_first_line = p_char == 0 && p_text.replace("\r", "") == "\n";
+ bool shift_first_line = p_char == 0 && substrings.size() == 2 && text_to_insert == "\n";
/* STEP 2: Add spaces if the char is greater than the end of the line. */
while (p_char > text[p_line].length()) {
@@ -6172,24 +6366,19 @@ void TextEdit::_base_insert_text(int p_line, int p_char, const String &p_text, i
}
/* STEP 3: Separate dest string in pre and post text. */
-
- String preinsert_text = text[p_line].substr(0, p_char);
String postinsert_text = text[p_line].substr(p_char, text[p_line].size());
- for (int j = 0; j < substrings.size(); j++) {
- // Insert the substrings.
-
- if (j == 0) {
- text.set(p_line, preinsert_text + substrings[j], structured_text_parser(st_parser, st_args, preinsert_text + substrings[j]));
- } else {
- text.insert(p_line + j, substrings[j], structured_text_parser(st_parser, st_args, substrings[j]));
- }
+ substrings.write[0] = text[p_line].substr(0, p_char) + substrings[0];
+ substrings.write[substrings.size() - 1] += postinsert_text;
- if (j == substrings.size() - 1) {
- text.set(p_line + j, text[p_line + j] + postinsert_text, structured_text_parser(st_parser, st_args, text[p_line + j] + postinsert_text));
- }
+ Vector<Array> bidi_override;
+ bidi_override.resize(substrings.size());
+ for (int i = 0; i < substrings.size(); i++) {
+ bidi_override.write[i] = structured_text_parser(st_parser, st_args, substrings[i]);
}
+ text.insert(p_line, substrings, bidi_override);
+
if (shift_first_line) {
text.move_gutters(p_line, p_line + 1);
text.set_hidden(p_line + 1, text.is_hidden(p_line));
@@ -6222,7 +6411,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
ERR_FAIL_COND_V(p_to_line < p_from_line, String()); // 'from > to'.
ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column < p_from_column, String()); // 'from > to'.
- String ret;
+ StringBuilder ret;
for (int i = p_from_line; i <= p_to_line; i++) {
int begin = (i == p_from_line) ? p_from_column : 0;
@@ -6234,7 +6423,7 @@ String TextEdit::_base_get_text(int p_from_line, int p_from_column, int p_to_lin
ret += text[i].substr(begin, end - begin);
}
- return ret;
+ return ret.as_string();
}
void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_line, int p_to_column) {
@@ -6248,9 +6437,7 @@ void TextEdit::_base_remove_text(int p_from_line, int p_from_column, int p_to_li
String pre_text = text[p_from_line].substr(0, p_from_column);
String post_text = text[p_to_line].substr(p_to_column, text[p_to_line].length());
- for (int i = p_from_line; i < p_to_line; i++) {
- text.remove_at(p_from_line + 1);
- }
+ text.remove_range(p_from_line, p_to_line);
text.set(p_from_line, pre_text + post_text, structured_text_parser(st_parser, st_args, pre_text + post_text));
if (!text_changed_dirty && !setting_text) {
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 42b21cbe9c..0d0f4f6dba 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -70,7 +70,7 @@ public:
GUTTER_TYPE_CUSTOM
};
- /* Contex Menu. */
+ /* Context Menu. */
enum MenuItems {
MENU_CUT,
MENU_COPY,
@@ -120,8 +120,7 @@ private:
bool clickable = false;
bool overwritable = false;
- ObjectID custom_draw_obj = ObjectID();
- StringName custom_draw_callback;
+ Callable custom_draw_callback;
};
class Text {
@@ -159,6 +158,7 @@ private:
mutable Vector<Line> text;
Ref<Font> font;
int font_size = -1;
+ int font_height = 0;
Dictionary opentype_features;
String language;
@@ -204,8 +204,8 @@ private:
}
}
bool is_hidden(int p_line) const { return text[p_line].hidden; }
- void insert(int p_at, const String &p_text, const Array &p_bidi_override);
- void remove_at(int p_index);
+ void insert(int p_at, const Vector<String> &p_text, const Vector<Array> &p_bidi_override);
+ void remove_range(int p_from_line, int p_to_line);
int size() const { return text.size(); }
void clear();
@@ -331,9 +331,7 @@ private:
int _get_column_pos_of_word(const String &p_key, const String &p_search, uint32_t p_search_flags, int p_from_column) const;
/* Tooltip. */
- ObjectID tooltip_obj_id;
- StringName tooltip_func;
- Variant tooltip_ud;
+ Callable tooltip_callback;
/* Mouse */
struct LineDrawingCache {
@@ -373,7 +371,10 @@ private:
bool move_caret_on_right_click = true;
- bool caret_mid_grapheme_enabled = false;
+ bool caret_mid_grapheme_enabled = true;
+
+ bool drag_action = false;
+ bool drag_caret_force_displayed = false;
void _emit_caret_changed();
@@ -400,6 +401,7 @@ private:
int to_column = 0;
bool shiftclick_left = false;
+ bool drag_attempt = false;
} selection;
bool selecting_enabled = true;
@@ -611,8 +613,11 @@ public:
virtual Size2 get_minimum_size() const override;
virtual bool is_text_field() const override;
virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override;
+ virtual Variant get_drag_data(const Point2 &p_point) override;
+ virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override;
+ virtual void drop_data(const Point2 &p_point, const Variant &p_data) override;
virtual String get_tooltip(const Point2 &p_pos) const override;
- void set_tooltip_request_func(Object *p_obj, const StringName &p_function, const Variant &p_udata);
+ void set_tooltip_request_func(const Callable &p_tooltip_callback);
/* Text */
// Text properties.
@@ -731,6 +736,7 @@ public:
int get_minimap_line_at_pos(const Point2i &p_pos) const;
bool is_dragging_cursor() const;
+ bool is_mouse_over_selection(bool p_edges = true) const;
/* Caret */
void set_caret_type(CaretType p_type);
@@ -875,7 +881,7 @@ public:
void merge_gutters(int p_from_line, int p_to_line);
- void set_gutter_custom_draw(int p_gutter, Object *p_object, const StringName &p_callback);
+ void set_gutter_custom_draw(int p_gutter, const Callable &p_draw_callback);
// Line gutters.
void set_line_gutter_metadata(int p_line, int p_gutter, const Variant &p_metadata);
diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp
index 8659ea06a2..da202c1c8f 100644
--- a/scene/gui/texture_button.cpp
+++ b/scene/gui/texture_button.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -170,6 +170,12 @@ void TextureButton::_notification(int p_what) {
Point2 ofs;
Size2 size;
+ bool draw_focus = (has_focus() && focused.is_valid());
+
+ // If no other texture is valid, try using focused texture.
+ if (!texdraw.is_valid() && draw_focus) {
+ texdraw = focused;
+ }
if (texdraw.is_valid()) {
size = texdraw->get_size();
@@ -226,7 +232,9 @@ void TextureButton::_notification(int p_what) {
size.width *= hflip ? -1.0f : 1.0f;
size.height *= vflip ? -1.0f : 1.0f;
- if (_tile) {
+ if (texdraw == focused) {
+ // Do nothing, we only needed to calculate the rectangle.
+ } else if (_tile) {
draw_texture_rect(texdraw, Rect2(ofs, size), _tile);
} else {
draw_texture_rect_region(texdraw, Rect2(ofs, size), _texture_region);
@@ -235,7 +243,7 @@ void TextureButton::_notification(int p_what) {
_position_rect = Rect2();
}
- if (has_focus() && focused.is_valid()) {
+ if (draw_focus) {
draw_texture_rect(focused, Rect2(ofs, size), false);
};
} break;
@@ -289,19 +297,19 @@ void TextureButton::_bind_methods() {
void TextureButton::set_normal_texture(const Ref<Texture2D> &p_normal) {
normal = p_normal;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_pressed_texture(const Ref<Texture2D> &p_pressed) {
pressed = p_pressed;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_hover_texture(const Ref<Texture2D> &p_hover) {
hover = p_hover;
update();
- minimum_size_changed();
+ update_minimum_size();
}
void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
@@ -312,7 +320,7 @@ void TextureButton::set_disabled_texture(const Ref<Texture2D> &p_disabled) {
void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) {
click_mask = p_click_mask;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureButton::get_normal_texture() const {
@@ -349,7 +357,7 @@ bool TextureButton::get_expand() const {
void TextureButton::set_expand(bool p_expand) {
expand = p_expand;
- minimum_size_changed();
+ update_minimum_size();
update();
}
diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h
index 8361f3c341..1428a79a1d 100644
--- a/scene/gui/texture_button.h
+++ b/scene/gui/texture_button.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp
index facbe06d4d..043c0f464c 100644
--- a/scene/gui/texture_progress_bar.cpp
+++ b/scene/gui/texture_progress_bar.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -35,7 +35,7 @@
void TextureProgressBar::set_under_texture(const Ref<Texture2D> &p_texture) {
under = p_texture;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureProgressBar::get_under_texture() const {
@@ -46,7 +46,7 @@ void TextureProgressBar::set_over_texture(const Ref<Texture2D> &p_texture) {
over = p_texture;
update();
if (under.is_null()) {
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -58,7 +58,7 @@ void TextureProgressBar::set_stretch_margin(Side p_side, int p_size) {
ERR_FAIL_INDEX((int)p_side, 4);
stretch_margin[p_side] = p_size;
update();
- minimum_size_changed();
+ update_minimum_size();
}
int TextureProgressBar::get_stretch_margin(Side p_side) const {
@@ -69,7 +69,7 @@ int TextureProgressBar::get_stretch_margin(Side p_side) const {
void TextureProgressBar::set_nine_patch_stretch(bool p_stretch) {
nine_patch_stretch = p_stretch;
update();
- minimum_size_changed();
+ update_minimum_size();
}
bool TextureProgressBar::get_nine_patch_stretch() const {
@@ -93,7 +93,7 @@ Size2 TextureProgressBar::get_minimum_size() const {
void TextureProgressBar::set_progress_texture(const Ref<Texture2D> &p_texture) {
progress = p_texture;
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureProgressBar::get_progress_texture() const {
@@ -501,6 +501,7 @@ void TextureProgressBar::_notification(int p_what) {
}
p *= get_relative_center();
+ p += progress_offset;
p = p.floor();
draw_line(p - Point2(8, 0), p + Point2(8, 0), Color(0.9, 0.5, 0.5), 2);
draw_line(p - Point2(0, 8), p + Point2(0, 8), Color(0.9, 0.5, 0.5), 2);
diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h
index c508f41387..4d3e38e006 100644
--- a/scene/gui/texture_progress_bar.h
+++ b/scene/gui/texture_progress_bar.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp
index 1cba88e06f..a8cdeb44f5 100644
--- a/scene/gui/texture_rect.cpp
+++ b/scene/gui/texture_rect.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -44,9 +44,6 @@ void TextureRect::_notification(int p_what) {
bool tile = false;
switch (stretch_mode) {
- case STRETCH_SCALE_ON_EXPAND: {
- size = expand ? get_size() : texture->get_size();
- } break;
case STRETCH_SCALE: {
size = get_size();
} break;
@@ -114,7 +111,7 @@ void TextureRect::_notification(int p_what) {
}
Size2 TextureRect::get_minimum_size() const {
- if (!expand && !texture.is_null()) {
+ if (!ignore_texture_size && !texture.is_null()) {
return texture->get_size();
} else {
return Size2();
@@ -124,8 +121,8 @@ Size2 TextureRect::get_minimum_size() const {
void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_texture", "texture"), &TextureRect::set_texture);
ClassDB::bind_method(D_METHOD("get_texture"), &TextureRect::get_texture);
- ClassDB::bind_method(D_METHOD("set_expand", "enable"), &TextureRect::set_expand);
- ClassDB::bind_method(D_METHOD("has_expand"), &TextureRect::has_expand);
+ ClassDB::bind_method(D_METHOD("set_ignore_texture_size", "ignore"), &TextureRect::set_ignore_texture_size);
+ ClassDB::bind_method(D_METHOD("get_ignore_texture_size"), &TextureRect::get_ignore_texture_size);
ClassDB::bind_method(D_METHOD("set_flip_h", "enable"), &TextureRect::set_flip_h);
ClassDB::bind_method(D_METHOD("is_flipped_h"), &TextureRect::is_flipped_h);
ClassDB::bind_method(D_METHOD("set_flip_v", "enable"), &TextureRect::set_flip_v);
@@ -134,12 +131,11 @@ void TextureRect::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureRect::get_stretch_mode);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand"), "set_expand", "has_expand");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale On Expand (Compat),Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_texture_size"), "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"), "set_flip_h", "is_flipped_h");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_v"), "set_flip_v", "is_flipped_v");
- BIND_ENUM_CONSTANT(STRETCH_SCALE_ON_EXPAND);
BIND_ENUM_CONSTANT(STRETCH_SCALE);
BIND_ENUM_CONSTANT(STRETCH_TILE);
BIND_ENUM_CONSTANT(STRETCH_KEEP);
@@ -152,7 +148,7 @@ void TextureRect::_bind_methods() {
void TextureRect::_texture_changed() {
if (texture.is_valid()) {
update();
- minimum_size_changed();
+ update_minimum_size();
}
}
@@ -172,21 +168,21 @@ void TextureRect::set_texture(const Ref<Texture2D> &p_tex) {
}
update();
- minimum_size_changed();
+ update_minimum_size();
}
Ref<Texture2D> TextureRect::get_texture() const {
return texture;
}
-void TextureRect::set_expand(bool p_expand) {
- expand = p_expand;
+void TextureRect::set_ignore_texture_size(bool p_ignore) {
+ ignore_texture_size = p_ignore;
update();
- minimum_size_changed();
+ update_minimum_size();
}
-bool TextureRect::has_expand() const {
- return expand;
+bool TextureRect::get_ignore_texture_size() const {
+ return ignore_texture_size;
}
void TextureRect::set_stretch_mode(StretchMode p_mode) {
diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h
index 0f93d5732f..7d667b25a8 100644
--- a/scene/gui/texture_rect.h
+++ b/scene/gui/texture_rect.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -38,7 +38,6 @@ class TextureRect : public Control {
public:
enum StretchMode {
- STRETCH_SCALE_ON_EXPAND, //default, for backwards compatibility
STRETCH_SCALE,
STRETCH_TILE,
STRETCH_KEEP,
@@ -49,11 +48,11 @@ public:
};
private:
- bool expand = false;
+ bool ignore_texture_size = false;
bool hflip = false;
bool vflip = false;
Ref<Texture2D> texture;
- StretchMode stretch_mode = STRETCH_SCALE_ON_EXPAND;
+ StretchMode stretch_mode = STRETCH_SCALE;
void _texture_changed();
@@ -66,8 +65,8 @@ public:
void set_texture(const Ref<Texture2D> &p_tex);
Ref<Texture2D> get_texture() const;
- void set_expand(bool p_expand);
- bool has_expand() const;
+ void set_ignore_texture_size(bool p_ignore);
+ bool get_ignore_texture_size() const;
void set_stretch_mode(StretchMode p_mode);
StretchMode get_stretch_mode() const;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 89caaaafd0..e75d147134 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -198,6 +198,65 @@ bool TreeItem::is_indeterminate(int p_column) const {
return cells[p_column].indeterminate;
}
+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);
+ }
+ _propagate_check_through_children(p_column, ch, p_emit_signal);
+ _propagate_check_through_parents(p_column, p_emit_signal);
+}
+
+void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal) {
+ TreeItem *current = get_first_child();
+ 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->_propagate_check_through_children(p_column, p_checked, p_emit_signal);
+ current = current->get_next();
+ }
+}
+
+void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal) {
+ TreeItem *current = get_parent();
+ if (!current) {
+ return;
+ }
+
+ bool all_unchecked_and_not_indeterminate = true;
+ bool any_unchecked_or_indeterminate = false;
+
+ TreeItem *child_item = current->get_first_child();
+ while (child_item) {
+ if (!child_item->is_checked(p_column)) {
+ any_unchecked_or_indeterminate = true;
+ if (child_item->is_indeterminate(p_column)) {
+ all_unchecked_and_not_indeterminate = false;
+ break;
+ }
+ } else {
+ all_unchecked_and_not_indeterminate = false;
+ }
+ child_item = child_item->get_next();
+ }
+
+ if (all_unchecked_and_not_indeterminate) {
+ current->set_checked(p_column, false);
+ } else if (any_unchecked_or_indeterminate) {
+ current->set_indeterminate(p_column, true);
+ } else {
+ current->set_checked(p_column, true);
+ }
+
+ if (p_emit_signal) {
+ current->tree->emit_signal("check_propagated_to_item", current, p_column);
+ }
+ current->_propagate_check_through_parents(p_column, p_emit_signal);
+}
+
void TreeItem::set_text(int p_column, String p_text) {
ERR_FAIL_INDEX(p_column, cells.size());
cells.write[p_column].text = p_text;
@@ -1002,18 +1061,18 @@ bool TreeItem::is_custom_set_as_button(int p_column) const {
return cells[p_column].custom_button;
}
-void TreeItem::set_text_align(int p_column, TextAlign p_align) {
+void TreeItem::set_text_alignment(int p_column, HorizontalAlignment p_alignment) {
ERR_FAIL_INDEX(p_column, cells.size());
- cells.write[p_column].text_align = p_align;
+ cells.write[p_column].text_alignment = p_alignment;
cells.write[p_column].cached_minimum_size_dirty = true;
_changed_notify(p_column);
}
-TreeItem::TextAlign TreeItem::get_text_align(int p_column) const {
- ERR_FAIL_INDEX_V(p_column, cells.size(), ALIGN_LEFT);
- return cells[p_column].text_align;
+HorizontalAlignment TreeItem::get_text_alignment(int p_column) const {
+ ERR_FAIL_INDEX_V(p_column, cells.size(), HORIZONTAL_ALIGNMENT_LEFT);
+ return cells[p_column].text_alignment;
}
void TreeItem::set_expand_right(int p_column, bool p_enable) {
@@ -1141,6 +1200,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_checked", "column"), &TreeItem::is_checked);
ClassDB::bind_method(D_METHOD("is_indeterminate", "column"), &TreeItem::is_indeterminate);
+ ClassDB::bind_method(D_METHOD("propagate_check", "column", "emit_signal"), &TreeItem::propagate_check, DEFVAL(true));
+
ClassDB::bind_method(D_METHOD("set_text", "column", "text"), &TreeItem::set_text);
ClassDB::bind_method(D_METHOD("get_text", "column"), &TreeItem::get_text);
@@ -1231,8 +1292,8 @@ void TreeItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tooltip", "column", "tooltip"), &TreeItem::set_tooltip);
ClassDB::bind_method(D_METHOD("get_tooltip", "column"), &TreeItem::get_tooltip);
- ClassDB::bind_method(D_METHOD("set_text_align", "column", "text_align"), &TreeItem::set_text_align);
- ClassDB::bind_method(D_METHOD("get_text_align", "column"), &TreeItem::get_text_align);
+ ClassDB::bind_method(D_METHOD("set_text_alignment", "column", "text_alignment"), &TreeItem::set_text_alignment);
+ ClassDB::bind_method(D_METHOD("get_text_alignment", "column"), &TreeItem::get_text_alignment);
ClassDB::bind_method(D_METHOD("set_expand_right", "column", "enable"), &TreeItem::set_expand_right);
ClassDB::bind_method(D_METHOD("get_expand_right", "column"), &TreeItem::get_expand_right);
@@ -1278,10 +1339,6 @@ void TreeItem::_bind_methods() {
BIND_ENUM_CONSTANT(CELL_MODE_RANGE);
BIND_ENUM_CONSTANT(CELL_MODE_ICON);
BIND_ENUM_CONSTANT(CELL_MODE_CUSTOM);
-
- BIND_ENUM_CONSTANT(ALIGN_LEFT);
- BIND_ENUM_CONSTANT(ALIGN_CENTER);
- BIND_ENUM_CONSTANT(ALIGN_RIGHT);
}
void TreeItem::clear_children() {
@@ -1477,16 +1534,17 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
}
w += ts.width;
- switch (p_cell.text_align) {
- case TreeItem::ALIGN_LEFT:
+ switch (p_cell.text_alignment) {
+ case HORIZONTAL_ALIGNMENT_FILL:
+ case HORIZONTAL_ALIGNMENT_LEFT: {
if (rtl) {
rect.position.x += MAX(0, (rect.size.width - w));
}
- break;
- case TreeItem::ALIGN_CENTER:
+ } break;
+ case HORIZONTAL_ALIGNMENT_CENTER:
rect.position.x += MAX(0, (rect.size.width - w) / 2);
break;
- case TreeItem::ALIGN_RIGHT:
+ case HORIZONTAL_ALIGNMENT_RIGHT:
if (!rtl) {
rect.position.x += MAX(0, (rect.size.width - w));
}
@@ -1539,7 +1597,7 @@ void Tree::update_column(int p_col) {
columns.write[p_col].text_buf->set_direction((TextServer::Direction)columns[p_col].text_direction);
}
- columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, (columns[p_col].language != "") ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ columns.write[p_col].text_buf->add_string(columns[p_col].title, cache.font, cache.font_size, columns[p_col].opentype_features, !columns[p_col].language.is_empty() ? columns[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
}
void Tree::update_item_cell(TreeItem *p_item, int p_col) {
@@ -1547,7 +1605,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
p_item->cells.write[p_col].text_buf->clear();
if (p_item->cells[p_col].mode == TreeItem::CELL_MODE_RANGE) {
- if (p_item->cells[p_col].text != "") {
+ if (!p_item->cells[p_col].text.is_empty()) {
if (!p_item->cells[p_col].editable) {
return;
}
@@ -1574,7 +1632,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
valtext = p_item->cells[p_col].text;
}
- if (p_item->cells[p_col].suffix != String()) {
+ if (!p_item->cells[p_col].suffix.is_empty()) {
valtext += " " + p_item->cells[p_col].suffix;
}
@@ -1597,7 +1655,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) {
} else {
font_size = cache.font_size;
}
- p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, (p_item->cells[p_col].language != "") ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
+ p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].opentype_features, !p_item->cells[p_col].language.is_empty() ? p_item->cells[p_col].language : TranslationServer::get_singleton()->get_tool_locale());
TS->shaped_text_set_bidi_override(p_item->cells[p_col].text_buf->get_rid(), structured_text_parser(p_item->cells[p_col].st_parser, p_item->cells[p_col].st_args, valtext));
p_item->cells.write[p_col].dirty = false;
}
@@ -1663,7 +1721,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (p_item->cells[i].expand_right) {
int plus = 1;
- while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text == "" && p_item->cells[i + plus].icon.is_null()) {
+ while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
w += get_column_width(i + plus);
plus++;
skip2++;
@@ -1860,7 +1918,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
} break;
case TreeItem::CELL_MODE_RANGE: {
- if (p_item->cells[i].text != "") {
+ if (!p_item->cells[i].text.is_empty()) {
if (!p_item->cells[i].editable) {
break;
}
@@ -2198,8 +2256,10 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c
*/
} else if (c.selected) {
- c.selected = false;
- //p_current->deselected_signal.call(p_col);
+ if (p_selected != p_current) {
+ // Deselect other rows.
+ c.selected = false;
+ }
}
} else if (select_mode == SELECT_SINGLE || select_mode == SELECT_MULTI) {
if (!r_in_range && &selected_cell == &c) {
@@ -2319,12 +2379,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
return -1;
}
- if (!p_item->disable_folding && !hide_folding && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) {
- if (p_item->first_child) {
- p_item->set_collapsed(!p_item->is_collapsed());
- }
-
- return -1; //handled!
+ if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + cache.item_margin))) {
+ p_item->set_collapsed(!p_item->is_collapsed());
+ return -1;
}
int x = p_pos.x;
@@ -2340,7 +2397,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
if (p_item->cells[i].expand_right) {
int plus = 1;
- while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text == "" && p_item->cells[i + plus].icon.is_null()) {
+ while (i + plus < columns.size() && !p_item->cells[i + plus].editable && p_item->cells[i + plus].mode == TreeItem::CELL_MODE_STRING && p_item->cells[i + plus].text.is_empty() && p_item->cells[i + plus].icon.is_null()) {
col_width += cache.hseparation;
col_width += get_column_width(i + plus);
plus++;
@@ -2523,7 +2580,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
} break;
case TreeItem::CELL_MODE_RANGE: {
- if (c.text != "") {
+ if (!c.text.is_empty()) {
//if (x >= (get_column_width(col)-item_h/2)) {
popup_menu->clear();
for (int i = 0; i < c.text.get_slice_count(","); i++) {
@@ -2532,7 +2589,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int
}
popup_menu->set_size(Size2(col_width, 0));
- popup_menu->set_position(get_global_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset);
+ popup_menu->set_position(get_screen_position() + Point2i(col_ofs, _get_title_button_height() + y_ofs + item_h) - cache.offset);
popup_menu->popup();
popup_edited_item = p_item;
popup_edited_item_col = col;
@@ -3175,7 +3232,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
if (drag_touching && !drag_touching_deaccel) {
drag_accum -= mm->get_relative().y;
v_scroll->set_value(drag_from + drag_accum);
- drag_speed = -mm->get_speed().y;
+ drag_speed = -mm->get_velocity().y;
}
}
@@ -3433,7 +3490,7 @@ bool Tree::edit_selected() {
item_edited(col, s);
return true;
- } else if (c.mode == TreeItem::CELL_MODE_RANGE && c.text != "") {
+ } else if (c.mode == TreeItem::CELL_MODE_RANGE && !c.text.is_empty()) {
popup_menu->clear();
for (int i = 0; i < c.text.get_slice_count(","); i++) {
String s2 = c.text.get_slicec(',', i);
@@ -3441,7 +3498,7 @@ bool Tree::edit_selected() {
}
popup_menu->set_size(Size2(rect.size.width, 0));
- popup_menu->set_position(get_global_position() + rect.position + Point2i(0, rect.size.height));
+ popup_menu->set_position(get_screen_position() + rect.position + Point2i(0, rect.size.height));
popup_menu->popup();
popup_edited_item = s;
popup_edited_item_col = col;
@@ -4370,7 +4427,7 @@ void Tree::scroll_to_item(TreeItem *p_item) {
void Tree::set_h_scroll_enabled(bool p_enable) {
h_scroll_enabled = p_enable;
- minimum_size_changed();
+ update_minimum_size();
}
bool Tree::is_h_scroll_enabled() const {
@@ -4379,7 +4436,7 @@ bool Tree::is_h_scroll_enabled() const {
void Tree::set_v_scroll_enabled(bool p_enable) {
v_scroll_enabled = p_enable;
- minimum_size_changed();
+ update_minimum_size();
}
bool Tree::is_v_scroll_enabled() const {
@@ -4682,7 +4739,7 @@ String Tree::get_tooltip(const Point2 &p_pos) const {
Size2 size = b->get_size() + cache.button_pressed->get_minimum_size();
if (pos.x > col_width - size.width) {
String tooltip = c.buttons[j].tooltip;
- if (tooltip != "") {
+ if (!tooltip.is_empty()) {
return tooltip;
}
}
@@ -4830,6 +4887,7 @@ void Tree::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_allow_reselect"), &Tree::get_allow_reselect);
ADD_PROPERTY(PropertyInfo(Variant::INT, "columns"), "set_columns", "get_columns");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "column_titles_visible"), "set_column_titles_visible", "are_column_titles_visible");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_reselect"), "set_allow_reselect", "get_allow_reselect");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");
@@ -4850,6 +4908,7 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("item_custom_button_pressed"));
ADD_SIGNAL(MethodInfo("item_double_clicked"));
ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem")));
+ ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column")));
//ADD_SIGNAL( MethodInfo("item_double_clicked" ) );
ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id")));
ADD_SIGNAL(MethodInfo("custom_popup_edited", PropertyInfo(Variant::BOOL, "arrow_clicked")));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index d4caec614a..33170cad35 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -52,12 +52,6 @@ public:
CELL_MODE_CUSTOM, ///< Contains a custom value, show a string, and an edit button
};
- enum TextAlign {
- ALIGN_LEFT,
- ALIGN_CENTER,
- ALIGN_RIGHT
- };
-
private:
friend class Tree;
@@ -98,7 +92,7 @@ private:
Size2i cached_minimum_size;
bool cached_minimum_size_dirty = true;
- TextAlign text_align = ALIGN_LEFT;
+ HorizontalAlignment text_alignment = HORIZONTAL_ALIGNMENT_LEFT;
Variant meta;
String tooltip;
@@ -218,6 +212,14 @@ public:
bool is_checked(int p_column) const;
bool is_indeterminate(int p_column) const;
+ void propagate_check(int p_column, bool p_emit_signal = true);
+
+private:
+ // Check helpers.
+ void _propagate_check_through_children(int p_column, bool p_checked, bool p_emit_signal);
+ void _propagate_check_through_parents(int p_column, bool p_emit_signal);
+
+public:
void set_text(int p_column, String p_text);
String get_text(int p_column) const;
@@ -316,8 +318,8 @@ public:
void set_tooltip(int p_column, const String &p_tooltip);
String get_tooltip(int p_column) const;
- void set_text_align(int p_column, TextAlign p_align);
- TextAlign get_text_align(int p_column) const;
+ void set_text_alignment(int p_column, HorizontalAlignment p_alignment);
+ HorizontalAlignment get_text_alignment(int p_column) const;
void set_expand_right(int p_column, bool p_enable);
bool get_expand_right(int p_column) const;
@@ -359,7 +361,6 @@ public:
};
VARIANT_ENUM_CAST(TreeItem::TreeCellMode);
-VARIANT_ENUM_CAST(TreeItem::TextAlign);
class VBoxContainer;
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_stream_player.cpp
index 989aabc549..1f2a8c8aa1 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* video_player.cpp */
+/* video_stream_player.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,13 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "video_player.h"
+#include "video_stream_player.h"
#include "core/os/os.h"
#include "scene/scene_string_names.h"
#include "servers/audio_server.h"
-int VideoPlayer::sp_get_channel_count() const {
+int VideoStreamPlayer::sp_get_channel_count() const {
if (playback.is_null()) {
return 0;
}
@@ -42,7 +42,7 @@ int VideoPlayer::sp_get_channel_count() const {
return playback->get_channels();
}
-bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) {
+bool VideoStreamPlayer::mix(AudioFrame *p_buffer, int p_frames) {
// Check the amount resampler can really handle.
// If it cannot, wait "wait_resampler_phase_limit" times.
// This mechanism contributes to smoother pause/unpause operation.
@@ -56,11 +56,11 @@ bool VideoPlayer::mix(AudioFrame *p_buffer, int p_frames) {
}
// Called from main thread (e.g. VideoStreamPlaybackTheora::update).
-int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
+int VideoStreamPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_frames) {
ERR_FAIL_NULL_V(p_udata, 0);
ERR_FAIL_NULL_V(p_data, 0);
- VideoPlayer *vp = (VideoPlayer *)p_udata;
+ VideoStreamPlayer *vp = (VideoStreamPlayer *)p_udata;
int todo = MIN(vp->resampler.get_writer_space(), p_frames);
@@ -75,13 +75,13 @@ int VideoPlayer::_audio_mix_callback(void *p_udata, const float *p_data, int p_f
return todo;
}
-void VideoPlayer::_mix_audios(void *p_self) {
+void VideoStreamPlayer::_mix_audios(void *p_self) {
ERR_FAIL_NULL(p_self);
- reinterpret_cast<VideoPlayer *>(p_self)->_mix_audio();
+ reinterpret_cast<VideoStreamPlayer *>(p_self)->_mix_audio();
}
// Called from audio thread
-void VideoPlayer::_mix_audio() {
+void VideoStreamPlayer::_mix_audio() {
if (!stream.is_valid()) {
return;
}
@@ -126,7 +126,7 @@ void VideoPlayer::_mix_audio() {
}
}
-void VideoPlayer::_notification(int p_notification) {
+void VideoStreamPlayer::_notification(int p_notification) {
switch (p_notification) {
case NOTIFICATION_ENTER_TREE: {
AudioServer::get_singleton()->add_mix_callback(_mix_audios, this);
@@ -180,7 +180,7 @@ void VideoPlayer::_notification(int p_notification) {
};
};
-Size2 VideoPlayer::get_minimum_size() const {
+Size2 VideoStreamPlayer::get_minimum_size() const {
if (!expand && !texture.is_null()) {
return texture->get_size();
} else {
@@ -188,17 +188,17 @@ Size2 VideoPlayer::get_minimum_size() const {
}
}
-void VideoPlayer::set_expand(bool p_expand) {
+void VideoStreamPlayer::set_expand(bool p_expand) {
expand = p_expand;
update();
- minimum_size_changed();
+ update_minimum_size();
}
-bool VideoPlayer::has_expand() const {
+bool VideoStreamPlayer::has_expand() const {
return expand;
}
-void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
+void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
AudioServer::get_singleton()->lock();
@@ -241,15 +241,15 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
update();
if (!expand) {
- minimum_size_changed();
+ update_minimum_size();
}
};
-Ref<VideoStream> VideoPlayer::get_stream() const {
+Ref<VideoStream> VideoStreamPlayer::get_stream() const {
return stream;
};
-void VideoPlayer::play() {
+void VideoStreamPlayer::play() {
ERR_FAIL_COND(!is_inside_tree());
if (playback.is_null()) {
return;
@@ -262,7 +262,7 @@ void VideoPlayer::play() {
last_audio_time = 0;
};
-void VideoPlayer::stop() {
+void VideoStreamPlayer::stop() {
if (!is_inside_tree()) {
return;
}
@@ -277,7 +277,7 @@ void VideoPlayer::stop() {
last_audio_time = 0;
};
-bool VideoPlayer::is_playing() const {
+bool VideoStreamPlayer::is_playing() const {
if (playback.is_null()) {
return false;
}
@@ -285,7 +285,7 @@ bool VideoPlayer::is_playing() const {
return playback->is_playing();
};
-void VideoPlayer::set_paused(bool p_paused) {
+void VideoStreamPlayer::set_paused(bool p_paused) {
paused = p_paused;
if (playback.is_valid()) {
playback->set_paused(p_paused);
@@ -294,35 +294,35 @@ void VideoPlayer::set_paused(bool p_paused) {
last_audio_time = 0;
};
-bool VideoPlayer::is_paused() const {
+bool VideoStreamPlayer::is_paused() const {
return paused;
}
-void VideoPlayer::set_buffering_msec(int p_msec) {
+void VideoStreamPlayer::set_buffering_msec(int p_msec) {
buffering_ms = p_msec;
}
-int VideoPlayer::get_buffering_msec() const {
+int VideoStreamPlayer::get_buffering_msec() const {
return buffering_ms;
}
-void VideoPlayer::set_audio_track(int p_track) {
+void VideoStreamPlayer::set_audio_track(int p_track) {
audio_track = p_track;
}
-int VideoPlayer::get_audio_track() const {
+int VideoStreamPlayer::get_audio_track() const {
return audio_track;
}
-void VideoPlayer::set_volume(float p_vol) {
+void VideoStreamPlayer::set_volume(float p_vol) {
volume = p_vol;
};
-float VideoPlayer::get_volume() const {
+float VideoStreamPlayer::get_volume() const {
return volume;
};
-void VideoPlayer::set_volume_db(float p_db) {
+void VideoStreamPlayer::set_volume_db(float p_db) {
if (p_db < -79) {
set_volume(0);
} else {
@@ -330,7 +330,7 @@ void VideoPlayer::set_volume_db(float p_db) {
}
};
-float VideoPlayer::get_volume_db() const {
+float VideoStreamPlayer::get_volume_db() const {
if (volume == 0) {
return -80;
} else {
@@ -338,27 +338,27 @@ float VideoPlayer::get_volume_db() const {
}
};
-String VideoPlayer::get_stream_name() const {
+String VideoStreamPlayer::get_stream_name() const {
if (stream.is_null()) {
return "<No Stream>";
}
return stream->get_name();
};
-float VideoPlayer::get_stream_position() const {
+float VideoStreamPlayer::get_stream_position() const {
if (playback.is_null()) {
return 0;
}
return playback->get_playback_position();
};
-void VideoPlayer::set_stream_position(float p_position) {
+void VideoStreamPlayer::set_stream_position(float p_position) {
if (playback.is_valid()) {
playback->seek(p_position);
}
}
-Ref<Texture2D> VideoPlayer::get_video_texture() const {
+Ref<Texture2D> VideoStreamPlayer::get_video_texture() const {
if (playback.is_valid()) {
return playback->get_texture();
}
@@ -366,22 +366,22 @@ Ref<Texture2D> VideoPlayer::get_video_texture() const {
return Ref<Texture2D>();
}
-void VideoPlayer::set_autoplay(bool p_enable) {
+void VideoStreamPlayer::set_autoplay(bool p_enable) {
autoplay = p_enable;
};
-bool VideoPlayer::has_autoplay() const {
+bool VideoStreamPlayer::has_autoplay() const {
return autoplay;
};
-void VideoPlayer::set_bus(const StringName &p_bus) {
+void VideoStreamPlayer::set_bus(const StringName &p_bus) {
//if audio is active, must lock this
AudioServer::get_singleton()->lock();
bus = p_bus;
AudioServer::get_singleton()->unlock();
}
-StringName VideoPlayer::get_bus() const {
+StringName VideoStreamPlayer::get_bus() const {
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
if (AudioServer::get_singleton()->get_bus_name(i) == bus) {
return bus;
@@ -390,7 +390,7 @@ StringName VideoPlayer::get_bus() const {
return "Master";
}
-void VideoPlayer::_validate_property(PropertyInfo &p_property) const {
+void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const {
if (p_property.name == "bus") {
String options;
for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) {
@@ -405,45 +405,45 @@ void VideoPlayer::_validate_property(PropertyInfo &p_property) const {
}
}
-void VideoPlayer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoPlayer::set_stream);
- ClassDB::bind_method(D_METHOD("get_stream"), &VideoPlayer::get_stream);
+void VideoStreamPlayer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_stream", "stream"), &VideoStreamPlayer::set_stream);
+ ClassDB::bind_method(D_METHOD("get_stream"), &VideoStreamPlayer::get_stream);
- ClassDB::bind_method(D_METHOD("play"), &VideoPlayer::play);
- ClassDB::bind_method(D_METHOD("stop"), &VideoPlayer::stop);
+ ClassDB::bind_method(D_METHOD("play"), &VideoStreamPlayer::play);
+ ClassDB::bind_method(D_METHOD("stop"), &VideoStreamPlayer::stop);
- ClassDB::bind_method(D_METHOD("is_playing"), &VideoPlayer::is_playing);
+ ClassDB::bind_method(D_METHOD("is_playing"), &VideoStreamPlayer::is_playing);
- ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoPlayer::set_paused);
- ClassDB::bind_method(D_METHOD("is_paused"), &VideoPlayer::is_paused);
+ ClassDB::bind_method(D_METHOD("set_paused", "paused"), &VideoStreamPlayer::set_paused);
+ ClassDB::bind_method(D_METHOD("is_paused"), &VideoStreamPlayer::is_paused);
- ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoPlayer::set_volume);
- ClassDB::bind_method(D_METHOD("get_volume"), &VideoPlayer::get_volume);
+ ClassDB::bind_method(D_METHOD("set_volume", "volume"), &VideoStreamPlayer::set_volume);
+ ClassDB::bind_method(D_METHOD("get_volume"), &VideoStreamPlayer::get_volume);
- ClassDB::bind_method(D_METHOD("set_volume_db", "db"), &VideoPlayer::set_volume_db);
- ClassDB::bind_method(D_METHOD("get_volume_db"), &VideoPlayer::get_volume_db);
+ ClassDB::bind_method(D_METHOD("set_volume_db", "db"), &VideoStreamPlayer::set_volume_db);
+ ClassDB::bind_method(D_METHOD("get_volume_db"), &VideoStreamPlayer::get_volume_db);
- ClassDB::bind_method(D_METHOD("set_audio_track", "track"), &VideoPlayer::set_audio_track);
- ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoPlayer::get_audio_track);
+ ClassDB::bind_method(D_METHOD("set_audio_track", "track"), &VideoStreamPlayer::set_audio_track);
+ ClassDB::bind_method(D_METHOD("get_audio_track"), &VideoStreamPlayer::get_audio_track);
- ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoPlayer::get_stream_name);
+ ClassDB::bind_method(D_METHOD("get_stream_name"), &VideoStreamPlayer::get_stream_name);
- ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoPlayer::set_stream_position);
- ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoPlayer::get_stream_position);
+ ClassDB::bind_method(D_METHOD("set_stream_position", "position"), &VideoStreamPlayer::set_stream_position);
+ ClassDB::bind_method(D_METHOD("get_stream_position"), &VideoStreamPlayer::get_stream_position);
- ClassDB::bind_method(D_METHOD("set_autoplay", "enabled"), &VideoPlayer::set_autoplay);
- ClassDB::bind_method(D_METHOD("has_autoplay"), &VideoPlayer::has_autoplay);
+ ClassDB::bind_method(D_METHOD("set_autoplay", "enabled"), &VideoStreamPlayer::set_autoplay);
+ ClassDB::bind_method(D_METHOD("has_autoplay"), &VideoStreamPlayer::has_autoplay);
- ClassDB::bind_method(D_METHOD("set_expand", "enable"), &VideoPlayer::set_expand);
- ClassDB::bind_method(D_METHOD("has_expand"), &VideoPlayer::has_expand);
+ ClassDB::bind_method(D_METHOD("set_expand", "enable"), &VideoStreamPlayer::set_expand);
+ ClassDB::bind_method(D_METHOD("has_expand"), &VideoStreamPlayer::has_expand);
- ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoPlayer::set_buffering_msec);
- ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoPlayer::get_buffering_msec);
+ ClassDB::bind_method(D_METHOD("set_buffering_msec", "msec"), &VideoStreamPlayer::set_buffering_msec);
+ ClassDB::bind_method(D_METHOD("get_buffering_msec"), &VideoStreamPlayer::get_buffering_msec);
- ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoPlayer::set_bus);
- ClassDB::bind_method(D_METHOD("get_bus"), &VideoPlayer::get_bus);
+ ClassDB::bind_method(D_METHOD("set_bus", "bus"), &VideoStreamPlayer::set_bus);
+ ClassDB::bind_method(D_METHOD("get_bus"), &VideoStreamPlayer::get_bus);
- ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoPlayer::get_video_texture);
+ ClassDB::bind_method(D_METHOD("get_video_texture"), &VideoStreamPlayer::get_video_texture);
ADD_SIGNAL(MethodInfo("finished"));
@@ -461,9 +461,9 @@ void VideoPlayer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus");
}
-VideoPlayer::VideoPlayer() {}
+VideoStreamPlayer::VideoStreamPlayer() {}
-VideoPlayer::~VideoPlayer() {
+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
diff --git a/scene/gui/video_player.h b/scene/gui/video_stream_player.h
index 0edad296a1..130b2901f1 100644
--- a/scene/gui/video_player.h
+++ b/scene/gui/video_stream_player.h
@@ -1,12 +1,12 @@
/*************************************************************************/
-/* video_player.h */
+/* video_stream_player.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -28,16 +28,16 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#ifndef VIDEO_PLAYER_H
-#define VIDEO_PLAYER_H
+#ifndef VIDEO_STREAM_PLAYER_H
+#define VIDEO_STREAM_PLAYER_H
#include "scene/gui/control.h"
#include "scene/resources/video_stream.h"
#include "servers/audio/audio_rb_resampler.h"
#include "servers/audio_server.h"
-class VideoPlayer : public Control {
- GDCLASS(VideoPlayer, Control);
+class VideoStreamPlayer : public Control {
+ GDCLASS(VideoStreamPlayer, Control);
struct Output {
AudioFrame vol;
@@ -119,8 +119,8 @@ public:
void set_bus(const StringName &p_bus);
StringName get_bus() const;
- VideoPlayer();
- ~VideoPlayer();
+ VideoStreamPlayer();
+ ~VideoStreamPlayer();
};
-#endif // VIDEO_PLAYER_H
+#endif // VIDEO_STREAM_PLAYER_H
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
new file mode 100644
index 0000000000..ba5e8d4a17
--- /dev/null
+++ b/scene/gui/view_panner.cpp
@@ -0,0 +1,142 @@
+/*************************************************************************/
+/* view_panner.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "view_panner.h"
+
+#include "core/input/input.h"
+#include "core/os/keyboard.h"
+
+bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) {
+ Ref<InputEventMouseButton> mb = p_event;
+ if (mb.is_valid()) {
+ // Alt modifier is unused, so ignore such events.
+ if (mb->is_alt_pressed()) {
+ return false;
+ }
+
+ Vector2i scroll_vec = Vector2((mb->get_button_index() == MouseButton::WHEEL_RIGHT) - (mb->get_button_index() == MouseButton::WHEEL_LEFT), (mb->get_button_index() == MouseButton::WHEEL_DOWN) - (mb->get_button_index() == MouseButton::WHEEL_UP));
+ if (scroll_vec != Vector2()) {
+ if (control_scheme == SCROLL_PANS) {
+ if (mb->is_ctrl_pressed()) {
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ return true;
+ } else {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, panning);
+ return true;
+ }
+ } else {
+ if (mb->is_ctrl_pressed()) {
+ Vector2 panning;
+ if (mb->is_shift_pressed()) {
+ panning.x += mb->get_factor() * scroll_vec.y;
+ panning.y += mb->get_factor() * scroll_vec.x;
+ } else {
+ panning.y += mb->get_factor() * scroll_vec.y;
+ panning.x += mb->get_factor() * scroll_vec.x;
+ }
+ callback_helper(scroll_callback, panning);
+ return true;
+ } else if (!mb->is_shift_pressed()) {
+ scroll_vec.y *= mb->get_factor();
+ callback_helper(zoom_callback, scroll_vec, mb->get_position());
+ return true;
+ }
+ }
+ }
+
+ if (mb->get_button_index() == MouseButton::MIDDLE || (mb->get_button_index() == MouseButton::RIGHT && !disable_rmb) || (mb->get_button_index() == MouseButton::LEFT && (Input::get_singleton()->is_key_pressed(Key::SPACE) || (is_dragging && !mb->is_pressed())))) {
+ if (mb->is_pressed()) {
+ is_dragging = true;
+ } else {
+ is_dragging = false;
+ }
+ return true;
+ }
+ }
+
+ Ref<InputEventMouseMotion> mm = p_event;
+ if (mm.is_valid()) {
+ if (is_dragging) {
+ if (p_canvas_rect != Rect2()) {
+ callback_helper(pan_callback, Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect));
+ } else {
+ callback_helper(pan_callback, mm->get_relative());
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ViewPanner::callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2) {
+ if (p_callback == zoom_callback) {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * 2);
+ Variant var1 = p_arg1;
+ argptr[0] = &var1;
+ Variant var2 = p_arg2;
+ argptr[1] = &var2;
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, 2, result, ce);
+ } else {
+ const Variant **argptr = (const Variant **)alloca(sizeof(Variant *));
+ Variant var = p_arg1;
+ argptr[0] = &var;
+
+ Variant result;
+ Callable::CallError ce;
+ p_callback.call(argptr, 1, result, ce);
+ }
+}
+
+void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) {
+ scroll_callback = p_scroll_callback;
+ pan_callback = p_pan_callback;
+ zoom_callback = p_zoom_callback;
+}
+
+void ViewPanner::set_control_scheme(ControlScheme p_scheme) {
+ control_scheme = p_scheme;
+}
+
+void ViewPanner::set_disable_rmb(bool p_disable) {
+ disable_rmb = p_disable;
+}
diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h
new file mode 100644
index 0000000000..0a92cb3dfd
--- /dev/null
+++ b/scene/gui/view_panner.h
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* view_panner.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef VIEW_PANNER_H
+#define VIEW_PANNER_H
+
+#include "core/object/ref_counted.h"
+
+class InputEvent;
+
+class ViewPanner : public RefCounted {
+ GDCLASS(ViewPanner, RefCounted);
+
+public:
+ enum ControlScheme {
+ SCROLL_ZOOMS,
+ SCROLL_PANS,
+ };
+
+private:
+ bool is_dragging = false;
+ bool disable_rmb = false;
+
+ Callable scroll_callback;
+ Callable pan_callback;
+ Callable zoom_callback;
+
+ void callback_helper(Callable p_callback, Vector2 p_arg1, Vector2 p_arg2 = Vector2());
+ ControlScheme control_scheme = SCROLL_ZOOMS;
+
+public:
+ void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback);
+ void set_control_scheme(ControlScheme p_scheme);
+ void set_disable_rmb(bool p_disable);
+
+ bool is_panning() const { return is_dragging; }
+
+ bool gui_input(const Ref<InputEvent> &p_ev, Rect2 p_canvas_rect = Rect2());
+};
+
+#endif // VIEW_PANNER_H