summaryrefslogtreecommitdiff
path: root/scene/gui
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui')
-rw-r--r--scene/gui/aspect_ratio_container.cpp12
-rw-r--r--scene/gui/code_edit.cpp4
-rw-r--r--scene/gui/color_picker.cpp129
-rw-r--r--scene/gui/color_picker.h12
-rw-r--r--scene/gui/control.cpp14
-rw-r--r--scene/gui/control.h3
-rw-r--r--scene/gui/dialogs.cpp4
-rw-r--r--scene/gui/file_dialog.cpp9
-rw-r--r--scene/gui/graph_edit.cpp17
-rw-r--r--scene/gui/label.cpp255
-rw-r--r--scene/gui/label.h7
-rw-r--r--scene/gui/line_edit.cpp12
-rw-r--r--scene/gui/option_button.cpp8
-rw-r--r--scene/gui/popup.cpp11
-rw-r--r--scene/gui/popup_menu.cpp4
-rw-r--r--scene/gui/rich_text_label.cpp110
-rw-r--r--scene/gui/rich_text_label.h12
-rw-r--r--scene/gui/split_container.cpp2
-rw-r--r--scene/gui/subviewport_container.cpp4
-rw-r--r--scene/gui/tab_bar.cpp2
-rw-r--r--scene/gui/text_edit.cpp4
-rw-r--r--scene/gui/tree.cpp9
-rw-r--r--scene/gui/video_stream_player.cpp7
-rw-r--r--scene/gui/video_stream_player.h1
-rw-r--r--scene/gui/view_panner.cpp2
25 files changed, 399 insertions, 255 deletions
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index 802189c374..94240ccead 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -30,6 +30,8 @@
#include "aspect_ratio_container.h"
+#include "scene/gui/texture_rect.h"
+
Size2 AspectRatioContainer::get_minimum_size() const {
Size2 ms;
for (int i = 0; i < get_child_count(); i++) {
@@ -113,6 +115,16 @@ void AspectRatioContainer::_notification(int p_what) {
if (c->is_set_as_top_level()) {
continue;
}
+
+ // Temporary fix for editor crash.
+ TextureRect *trect = Object::cast_to<TextureRect>(c);
+ if (trect) {
+ if (trect->get_expand_mode() == TextureRect::EXPAND_FIT_WIDTH_PROPORTIONAL || trect->get_expand_mode() == TextureRect::EXPAND_FIT_HEIGHT_PROPORTIONAL) {
+ WARN_PRINT_ONCE("Proportional TextureRect is currently not supported inside AspectRatioContainer");
+ continue;
+ }
+ }
+
Size2 child_minsize = c->get_combined_minimum_size();
Size2 child_size = Size2(ratio, 1.0);
float scale_factor = 1.0;
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index c977d9d2fb..e2f7ec860c 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -2856,7 +2856,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
const int caret_line = get_caret_line();
const int caret_column = get_caret_column();
const String line = get_line(caret_line);
- ERR_FAIL_INDEX_MSG(caret_column - 1, line.length(), "Caret column exceeds line length.");
+ ERR_FAIL_INDEX_MSG(caret_column, line.length() + 1, "Caret column exceeds line length.");
if (caret_column > 0 && line[caret_column - 1] == '(' && !code_completion_forced) {
cancel_code_completion();
@@ -3088,6 +3088,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() {
}
code_completion_options.append_array(completion_options_casei);
+ code_completion_options.append_array(completion_options_substr);
+ code_completion_options.append_array(completion_options_substr_casei);
code_completion_options.append_array(completion_options_subseq);
code_completion_options.append_array(completion_options_subseq_casei);
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index da29bc823f..48e3759981 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -31,10 +31,12 @@
#include "color_picker.h"
#include "core/input/input.h"
+#include "core/io/image.h"
#include "core/math/color.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "scene/gui/color_mode.h"
+#include "servers/display_server.h"
#include "thirdparty/misc/ok_color.h"
#include "thirdparty/misc/ok_color_shader.h"
@@ -90,8 +92,8 @@ void ColorPicker::_notification(int p_what) {
} break;
case NOTIFICATION_WM_CLOSE_REQUEST: {
- if (screen != nullptr && screen->is_visible()) {
- screen->hide();
+ if (picker_window != nullptr && picker_window->is_visible()) {
+ picker_window->hide();
}
} break;
}
@@ -1197,11 +1199,11 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
}
if (!spinning) {
- real_t x = CLAMP(bev->get_position().x, corner_x, c->get_size().x - corner_x);
- real_t y = CLAMP(bev->get_position().y, corner_x, c->get_size().y - corner_y);
+ real_t x = CLAMP(bev->get_position().x - corner_x, 0, real_size.x);
+ real_t y = CLAMP(bev->get_position().y - corner_y, 0, real_size.y);
- s = (x - c->get_position().x - corner_x) / real_size.x;
- v = 1.0 - (y - c->get_position().y - corner_y) / real_size.y;
+ s = x / real_size.x;
+ v = 1.0 - y / real_size.y;
}
}
@@ -1250,11 +1252,11 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) {
real_t corner_y = (c == wheel_uv) ? center.y - Math_SQRT12 * c->get_size().height * 0.42 : 0;
Size2 real_size(c->get_size().x - corner_x * 2, c->get_size().y - corner_y * 2);
- real_t x = CLAMP(mev->get_position().x, corner_x, c->get_size().x - corner_x);
- real_t y = CLAMP(mev->get_position().y, corner_x, c->get_size().y - corner_y);
+ real_t x = CLAMP(mev->get_position().x - corner_x, 0, real_size.x);
+ real_t y = CLAMP(mev->get_position().y - corner_y, 0, real_size.y);
- s = (x - corner_x) / real_size.x;
- v = 1.0 - (y - corner_y) / real_size.y;
+ s = x / real_size.x;
+ v = 1.0 - y / real_size.y;
}
}
@@ -1372,30 +1374,26 @@ void ColorPicker::_recent_preset_pressed(const bool p_pressed, ColorPresetButton
emit_signal(SNAME("color_changed"), p_preset->get_preset_color());
}
-void ColorPicker::_screen_input(const Ref<InputEvent> &p_event) {
+void ColorPicker::_picker_texture_input(const Ref<InputEvent> &p_event) {
if (!is_inside_tree()) {
return;
}
Ref<InputEventMouseButton> bev = p_event;
if (bev.is_valid() && bev->get_button_index() == MouseButton::LEFT && !bev->is_pressed()) {
+ set_pick_color(picker_color);
emit_signal(SNAME("color_changed"), color);
- screen->hide();
+ picker_window->hide();
}
Ref<InputEventMouseMotion> mev = p_event;
if (mev.is_valid()) {
- Viewport *r = get_tree()->get_root();
- if (!r->get_visible_rect().has_point(mev->get_global_position())) {
- return;
- }
-
- Ref<Image> img = r->get_texture()->get_image();
+ Ref<Image> img = picker_texture_rect->get_texture()->get_image();
if (img.is_valid() && !img->is_empty()) {
- Vector2 ofs = mev->get_global_position();
- Color c = img->get_pixel(ofs.x, ofs.y);
-
- set_pick_color(c);
+ Vector2 ofs = mev->get_position();
+ picker_color = img->get_pixel(ofs.x, ofs.y);
+ picker_preview_style_box->set_bg_color(picker_color);
+ picker_preview_label->set_self_modulate(picker_color.get_luminance() < 0.5 ? Color(1.0f, 1.0f, 1.0f) : Color(0.0f, 0.0f, 0.0f));
}
}
}
@@ -1409,27 +1407,79 @@ void ColorPicker::_add_preset_pressed() {
emit_signal(SNAME("preset_added"), color);
}
-void ColorPicker::_screen_pick_pressed() {
+void ColorPicker::_pick_button_pressed() {
if (!is_inside_tree()) {
return;
}
- Viewport *r = get_tree()->get_root();
- if (!screen) {
- screen = memnew(Control);
- r->add_child(screen);
- screen->set_as_top_level(true);
- screen->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
- screen->set_default_cursor_shape(CURSOR_POINTING_HAND);
- screen->connect("gui_input", callable_mp(this, &ColorPicker::_screen_input));
- // It immediately toggles off in the first press otherwise.
- screen->call_deferred(SNAME("connect"), "hidden", Callable(btn_pick, "set_pressed").bind(false));
+ if (!picker_window) {
+ picker_window = memnew(Popup);
+ picker_window->hide();
+ picker_window->set_transient(true);
+ add_child(picker_window);
+
+ picker_texture_rect = memnew(TextureRect);
+ picker_texture_rect->set_anchors_preset(Control::PRESET_FULL_RECT);
+ picker_window->add_child(picker_texture_rect);
+ picker_texture_rect->set_default_cursor_shape(CURSOR_POINTING_HAND);
+ picker_texture_rect->connect(SNAME("gui_input"), callable_mp(this, &ColorPicker::_picker_texture_input));
+
+ picker_preview = memnew(Panel);
+ picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
+ picker_preview->set_mouse_filter(MOUSE_FILTER_IGNORE);
+ picker_window->add_child(picker_preview);
+
+ picker_preview_label = memnew(Label);
+ picker_preview->set_anchors_preset(Control::PRESET_CENTER_TOP);
+ picker_preview_label->set_text("Color Picking active");
+ picker_preview->add_child(picker_preview_label);
+
+ picker_preview_style_box = (Ref<StyleBoxFlat>)memnew(StyleBoxFlat);
+ picker_preview_style_box->set_bg_color(Color(1.0, 1.0, 1.0));
+ picker_preview->add_theme_style_override("panel", picker_preview_style_box);
+ }
+
+ Rect2i screen_rect;
+ if (picker_window->is_embedded()) {
+ screen_rect = picker_window->get_embedder()->get_visible_rect();
+ picker_window->set_position(Point2i());
+ picker_texture_rect->set_texture(ImageTexture::create_from_image(picker_window->get_embedder()->get_texture()->get_image()));
} else {
- screen->show();
+ screen_rect = picker_window->get_parent_rect();
+ picker_window->set_position(screen_rect.position);
+
+ Ref<Image> target_image = Image::create_empty(screen_rect.size.x, screen_rect.size.y, false, Image::FORMAT_RGB8);
+ DisplayServer *ds = DisplayServer::get_singleton();
+
+ // Add the Texture of each Window to the Image.
+ Vector<DisplayServer::WindowID> wl = ds->get_window_list();
+ // FIXME: sort windows by visibility.
+ for (int index = 0; index < wl.size(); index++) {
+ DisplayServer::WindowID wid = wl[index];
+ if (wid == DisplayServer::INVALID_WINDOW_ID) {
+ continue;
+ }
+
+ ObjectID woid = DisplayServer::get_singleton()->window_get_attached_instance_id(wid);
+ if (woid == ObjectID()) {
+ continue;
+ }
+
+ Window *w = Object::cast_to<Window>(ObjectDB::get_instance(woid));
+ Ref<Image> img = w->get_texture()->get_image();
+ if (!img.is_valid() || img->is_empty()) {
+ continue;
+ }
+ img->convert(Image::FORMAT_RGB8);
+ target_image->blit_rect(img, Rect2i(Point2i(0, 0), img->get_size()), w->get_position());
+ }
+
+ picker_texture_rect->set_texture(ImageTexture::create_from_image(target_image));
}
- screen->move_to_front();
- // TODO: show modal no longer works, needs to be converted to a popup.
- //screen->show_modal();
+
+ picker_window->set_size(screen_rect.size);
+ picker_preview->set_size(screen_rect.size / 10.0); // 10% of size in each axis.
+ picker_window->popup();
}
void ColorPicker::_html_focus_exit() {
@@ -1595,9 +1645,8 @@ ColorPicker::ColorPicker() {
btn_pick = memnew(Button);
sample_hbc->add_child(btn_pick);
- btn_pick->set_toggle_mode(true);
- btn_pick->set_tooltip_text(RTR("Pick a color from the editor window."));
- btn_pick->connect("pressed", callable_mp(this, &ColorPicker::_screen_pick_pressed));
+ btn_pick->set_tooltip_text(RTR("Pick a color from the application window."));
+ btn_pick->connect(SNAME("pressed"), callable_mp(this, &ColorPicker::_pick_button_pressed));
sample = memnew(TextureRect);
sample_hbc->add_child(sample);
diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h
index f7578612cd..d02c3278e6 100644
--- a/scene/gui/color_picker.h
+++ b/scene/gui/color_picker.h
@@ -40,6 +40,7 @@
#include "scene/gui/line_edit.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/option_button.h"
+#include "scene/gui/panel.h"
#include "scene/gui/popup.h"
#include "scene/gui/separator.h"
#include "scene/gui/slider.h"
@@ -111,7 +112,12 @@ private:
Vector<ColorMode *> modes;
- Control *screen = nullptr;
+ Popup *picker_window = nullptr;
+ TextureRect *picker_texture_rect = nullptr;
+ Panel *picker_preview = nullptr;
+ Label *picker_preview_label = nullptr;
+ Ref<StyleBoxFlat> picker_preview_style_box;
+ Color picker_color;
Control *uv_edit = nullptr;
Control *w_edit = nullptr;
AspectRatioContainer *wheel_edit = nullptr;
@@ -211,10 +217,10 @@ private:
void _line_edit_input(const Ref<InputEvent> &p_event);
void _preset_input(const Ref<InputEvent> &p_event, const Color &p_color);
void _recent_preset_pressed(const bool pressed, ColorPresetButton *p_preset);
- void _screen_input(const Ref<InputEvent> &p_event);
+ void _picker_texture_input(const Ref<InputEvent> &p_event);
void _text_changed(const String &p_new_text);
void _add_preset_pressed();
- void _screen_pick_pressed();
+ void _pick_button_pressed();
void _html_focus_exit();
inline int _get_preset_size();
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index f7c056316d..ec75fcb665 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -644,8 +644,10 @@ Rect2 Control::get_parent_anchorable_rect() const {
parent_rect = data.parent_canvas_item->get_anchorable_rect();
} else {
#ifdef TOOLS_ENABLED
- Node *edited_root = get_tree()->get_edited_scene_root();
- if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) {
+ Node *edited_scene_root = get_tree()->get_edited_scene_root();
+ Node *scene_root_parent = edited_scene_root ? edited_scene_root->get_parent() : nullptr;
+
+ if (scene_root_parent && get_viewport() == scene_root_parent->get_viewport()) {
parent_rect.size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height"));
} else {
parent_rect = get_viewport()->get_visible_rect();
@@ -692,6 +694,12 @@ Transform2D Control::get_transform() const {
return xform;
}
+void Control::_top_level_changed_on_parent() {
+ // Update root control status.
+ _notification(NOTIFICATION_EXIT_CANVAS);
+ _notification(NOTIFICATION_ENTER_CANVAS);
+}
+
/// Anchors and offsets.
void Control::_set_anchor(Side p_side, real_t p_anchor) {
@@ -3384,7 +3392,7 @@ void Control::_bind_methods() {
ADD_SIGNAL(MethodInfo("minimum_size_changed"));
ADD_SIGNAL(MethodInfo("theme_changed"));
- GDVIRTUAL_BIND(_has_point, "position");
+ GDVIRTUAL_BIND(_has_point, "point");
GDVIRTUAL_BIND(_structured_text_parser, "args", "text");
GDVIRTUAL_BIND(_get_minimum_size);
diff --git a/scene/gui/control.h b/scene/gui/control.h
index a93a88e5b4..2fb5d559b6 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -292,6 +292,9 @@ private:
void _update_minimum_size();
void _size_changed();
+ void _top_level_changed() override {} // Controls don't need to do anything, only other CanvasItems.
+ void _top_level_changed_on_parent() override;
+
void _clear_size_warning();
// Input events.
diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp
index dfe9ea3b08..760c86b2cc 100644
--- a/scene/gui/dialogs.cpp
+++ b/scene/gui/dialogs.cpp
@@ -276,10 +276,6 @@ Size2 AcceptDialog::_get_contents_minimum_size() const {
// Plus there is a separation size added on top.
content_minsize.y += theme_cache.buttons_separation;
- // Last, we make sure that we aren't below the minimum window size.
- Size2 window_minsize = get_min_size();
- content_minsize.x = MAX(window_minsize.x, content_minsize.x);
- content_minsize.y = MAX(window_minsize.y, content_minsize.y);
return content_minsize;
}
diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp
index 5dd8cde342..ee065cccbf 100644
--- a/scene/gui/file_dialog.cpp
+++ b/scene/gui/file_dialog.cpp
@@ -347,14 +347,15 @@ void FileDialog::_action_pressed() {
}
}
- if (!valid) {
+ String file_name = file_text.strip_edges().get_file();
+ if (!valid || file_name.is_empty()) {
exterr->popup_centered(Size2(250, 80));
return;
}
if (dir_access->file_exists(f)) {
- confirm_save->set_text(RTR("File exists, overwrite?"));
- confirm_save->popup_centered(Size2(200, 80));
+ confirm_save->set_text(vformat(RTR("File \"%s\" already exists.\nDo you want to overwrite it?"), f));
+ confirm_save->popup_centered(Size2(250, 80));
} else {
emit_signal(SNAME("file_selected"), f);
hide();
@@ -1136,7 +1137,7 @@ FileDialog::FileDialog() {
add_child(mkdirerr, false, INTERNAL_MODE_FRONT);
exterr = memnew(AcceptDialog);
- exterr->set_text(RTR("Must use a valid extension."));
+ exterr->set_text(RTR("Invalid extension, or empty filename."));
add_child(exterr, false, INTERNAL_MODE_FRONT);
update_filters();
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index af52f6664a..58b820c31f 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -193,7 +193,7 @@ void GraphEditMinimap::_adjust_graph_scroll(const Vector2 &p_offset) {
PackedStringArray GraphEdit::get_configuration_warnings() const {
PackedStringArray warnings = Control::get_configuration_warnings();
- warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future beta version involving compatibility-breaking API changes."));
+ warnings.push_back(RTR("Please be aware that GraphEdit and GraphNode will undergo extensive refactoring in a future 4.x version involving compatibility-breaking API changes."));
return warnings;
}
@@ -264,10 +264,6 @@ void GraphEdit::_scroll_moved(double) {
top_layer->queue_redraw();
minimap->queue_redraw();
queue_redraw();
-
- if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
- emit_signal(SNAME("scroll_offset_changed"), get_scroll_ofs());
- }
}
void GraphEdit::_update_scroll_offset() {
@@ -290,6 +286,10 @@ void GraphEdit::_update_scroll_offset() {
connections_layer->set_position(-Point2(h_scroll->get_value(), v_scroll->get_value()));
set_block_minimum_size_adjust(false);
awaiting_scroll_offset_update = false;
+
+ if (!setting_scroll_ofs) { //in godot, signals on change value are avoided as a convention
+ emit_signal(SNAME("scroll_offset_changed"), get_scroll_ofs());
+ }
}
void GraphEdit::_update_scroll() {
@@ -860,7 +860,7 @@ bool GraphEdit::is_in_port_hotzone(const Vector2 &pos, const Vector2 &p_mouse_po
}
for (int i = 0; i < get_child_count(); i++) {
- Control *child = Object::cast_to<Control>(get_child(i));
+ GraphNode *child = Object::cast_to<GraphNode>(get_child(i));
if (!child) {
continue;
}
@@ -920,7 +920,8 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from
scaled_points.push_back(points[i] * p_zoom);
}
- p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased);
+ // Thickness below 0.5 doesn't look good on the graph or its minimap.
+ p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased);
}
void GraphEdit::_connections_layer_draw() {
@@ -1088,7 +1089,7 @@ void GraphEdit::_minimap_draw() {
from_color = from_color.lerp(activity_color, E.activity);
to_color = to_color.lerp(activity_color, E.activity);
}
- _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
+ _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length());
}
// Draw the "camera" viewport.
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index f59702835c..b861d7af01 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -86,7 +86,7 @@ int Label::get_line_height(int p_line) const {
}
}
-void Label::_shape() {
+bool Label::_shape() {
Ref<StyleBox> style = theme_cache.normal_style;
int width = (get_size().width - style->get_minimum_size().width);
@@ -101,7 +101,7 @@ void Label::_shape() {
}
const Ref<Font> &font = (settings.is_valid() && settings->get_font().is_valid()) ? settings->get_font() : theme_cache.font;
int font_size = settings.is_valid() ? settings->get_font_size() : theme_cache.font_size;
- ERR_FAIL_COND(font.is_null());
+ ERR_FAIL_COND_V(font.is_null(), true);
String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
txt = txt.substr(0, visible_chars);
@@ -121,6 +121,7 @@ void Label::_shape() {
dirty = false;
font_dirty = false;
lines_dirty = true;
+ // Note for future maintainers: forgetting stable width here (e.g., setting it to -1) may fix still undiscovered bugs.
}
if (lines_dirty) {
@@ -128,127 +129,143 @@ void Label::_shape() {
TS->free_rid(lines_rid[i]);
}
lines_rid.clear();
-
- BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
- switch (autowrap_mode) {
- case TextServer::AUTOWRAP_WORD_SMART:
- autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
- break;
- case TextServer::AUTOWRAP_WORD:
- autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
- break;
- case TextServer::AUTOWRAP_ARBITRARY:
- autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
- break;
- case TextServer::AUTOWRAP_OFF:
- break;
- }
- autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
-
- PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
- for (int i = 0; i < line_breaks.size(); i = i + 2) {
- RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
- lines_rid.push_back(line);
- }
}
+ Size2i prev_minsize = minsize;
+ minsize = Size2();
+
+ bool can_process_lines = false;
if (xl_text.length() == 0) {
- minsize = Size2(1, get_line_height());
- return;
- }
+ can_process_lines = true;
+ lines_dirty = false;
+ } else {
+ // With autowrap on or off with trimming enabled, we won't compute the minimum size until width is stable
+ // (two shape requests in a row with the same width.) This avoids situations in which the initial width is
+ // very narrow and the label would break text into many very short lines, causing a very tall label that can
+ // leave a deformed container. In the remaining case (namely, autowrap off and no trimming), the label is
+ // free to dictate its own width, something that will be taken advtantage of.
+ bool can_dictate_width = autowrap_mode == TextServer::AUTOWRAP_OFF && overrun_behavior == TextServer::OVERRUN_NO_TRIMMING;
+ bool is_width_stable = get_size().width == stable_width;
+ can_process_lines = can_dictate_width || is_width_stable;
+ stable_width = get_size().width;
+
+ if (can_process_lines) {
+ if (lines_dirty) {
+ BitField<TextServer::LineBreakFlag> autowrap_flags = TextServer::BREAK_MANDATORY;
+ switch (autowrap_mode) {
+ case TextServer::AUTOWRAP_WORD_SMART:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_ADAPTIVE | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_WORD:
+ autowrap_flags = TextServer::BREAK_WORD_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_ARBITRARY:
+ autowrap_flags = TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_MANDATORY;
+ break;
+ case TextServer::AUTOWRAP_OFF:
+ break;
+ }
+ autowrap_flags = autowrap_flags | TextServer::BREAK_TRIM_EDGE_SPACES;
- if (autowrap_mode == TextServer::AUTOWRAP_OFF) {
- minsize.width = 0.0f;
- for (int i = 0; i < lines_rid.size(); i++) {
- if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) {
- minsize.width = TS->shaped_text_get_size(lines_rid[i]).x;
+ PackedInt32Array line_breaks = TS->shaped_text_get_line_breaks(text_rid, width, 0, autowrap_flags);
+ for (int i = 0; i < line_breaks.size(); i = i + 2) {
+ RID line = TS->shaped_text_substr(text_rid, line_breaks[i], line_breaks[i + 1] - line_breaks[i]);
+ lines_rid.push_back(line);
+ }
}
- }
- }
- if (lines_dirty) {
- BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM;
- switch (overrun_behavior) {
- case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS:
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
- overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
- break;
- case TextServer::OVERRUN_TRIM_ELLIPSIS:
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
- overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
- break;
- case TextServer::OVERRUN_TRIM_WORD:
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
- break;
- case TextServer::OVERRUN_TRIM_CHAR:
- overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
- break;
- case TextServer::OVERRUN_NO_TRIMMING:
- break;
- }
-
- // Fill after min_size calculation.
-
- if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
- int visible_lines = get_visible_line_count();
- bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size();
- if (lines_hidden) {
- overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
- }
- if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ if (can_dictate_width) {
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);
- } else if (i == (visible_lines - 1)) {
- TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+ if (minsize.width < TS->shaped_text_get_size(lines_rid[i]).x) {
+ minsize.width = TS->shaped_text_get_size(lines_rid[i]).x;
}
}
- } else if (lines_hidden) {
- TS->shaped_text_overrun_trim_to_width(lines_rid[visible_lines - 1], width, overrun_flags);
+
+ width = (minsize.width - style->get_minimum_size().width);
}
- } else {
- // Autowrap disabled.
- for (int i = 0; i < lines_rid.size(); i++) {
- if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
- TS->shaped_text_fit_to_width(lines_rid[i], width);
- overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
- TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
- TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
+
+ if (lines_dirty) {
+ BitField<TextServer::TextOverrunFlag> overrun_flags = TextServer::OVERRUN_NO_TRIM;
+ switch (overrun_behavior) {
+ case TextServer::OVERRUN_TRIM_WORD_ELLIPSIS:
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
+ overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
+ break;
+ case TextServer::OVERRUN_TRIM_ELLIPSIS:
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
+ overrun_flags.set_flag(TextServer::OVERRUN_ADD_ELLIPSIS);
+ break;
+ case TextServer::OVERRUN_TRIM_WORD:
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM_WORD_ONLY);
+ break;
+ case TextServer::OVERRUN_TRIM_CHAR:
+ overrun_flags.set_flag(TextServer::OVERRUN_TRIM);
+ break;
+ case TextServer::OVERRUN_NO_TRIMMING:
+ break;
+ }
+
+ // Fill after min_size calculation.
+
+ int visible_lines = lines_rid.size();
+ if (max_lines_visible >= 0 && visible_lines > max_lines_visible) {
+ visible_lines = max_lines_visible;
+ }
+ if (autowrap_mode != TextServer::AUTOWRAP_OFF) {
+ bool lines_hidden = visible_lines > 0 && visible_lines < lines_rid.size();
+ if (lines_hidden) {
+ overrun_flags.set_flag(TextServer::OVERRUN_ENFORCE_ELLIPSIS);
+ }
+ 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);
+ } else if (i == (visible_lines - 1)) {
+ 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 {
- TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ // Autowrap disabled.
+ for (int i = 0; i < lines_rid.size(); i++) {
+ if (horizontal_alignment == HORIZONTAL_ALIGNMENT_FILL) {
+ TS->shaped_text_fit_to_width(lines_rid[i], width);
+ overrun_flags.set_flag(TextServer::OVERRUN_JUSTIFICATION_AWARE);
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ TS->shaped_text_fit_to_width(lines_rid[i], width, TextServer::JUSTIFICATION_WORD_BOUND | TextServer::JUSTIFICATION_KASHIDA | TextServer::JUSTIFICATION_CONSTRAIN_ELLIPSIS);
+ } else {
+ TS->shaped_text_overrun_trim_to_width(lines_rid[i], width, overrun_flags);
+ }
+ }
}
+
+ int last_line = MIN(lines_rid.size(), visible_lines + lines_skipped);
+ int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing;
+ for (int64_t i = lines_skipped; i < last_line; i++) {
+ minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
+ }
+
+ lines_dirty = false;
}
+ } else {
+ callable_mp(this, &Label::_shape).call_deferred();
}
- lines_dirty = false;
- lines_shaped_last_width = get_size().width;
}
- _update_visible();
-
- if (autowrap_mode == TextServer::AUTOWRAP_OFF || !clip || overrun_behavior == TextServer::OVERRUN_NO_TRIMMING) {
- update_minimum_size();
+ if (draw_pending) {
+ queue_redraw();
+ draw_pending = false;
}
-}
-void Label::_update_visible() {
- int line_spacing = settings.is_valid() ? settings->get_line_spacing() : theme_cache.line_spacing;
- Ref<StyleBox> style = theme_cache.normal_style;
- int lines_visible = lines_rid.size();
-
- if (max_lines_visible >= 0 && lines_visible > max_lines_visible) {
- lines_visible = max_lines_visible;
+ if (minsize != prev_minsize) {
+ update_minimum_size();
}
- minsize.height = 0;
- int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped);
- for (int64_t i = lines_skipped; i < last_line; i++) {
- minsize.height += TS->shaped_text_get_size(lines_rid[i]).y + line_spacing;
- if (minsize.height > (get_size().height - style->get_minimum_size().height + line_spacing)) {
- break;
- }
- }
+ return can_process_lines;
}
inline void draw_glyph(const Glyph &p_gl, const RID &p_canvas, const Color &p_font_color, const Vector2 &p_ofs) {
@@ -345,8 +362,24 @@ void Label::_notification(int p_what) {
RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true);
}
+ // When a shaped text is invalidated by an external source, we want to reshape it.
+ if (!TS->shaped_text_is_ready(text_rid)) {
+ dirty = true;
+ }
+
+ for (const RID &line_rid : lines_rid) {
+ if (!TS->shaped_text_is_ready(line_rid)) {
+ lines_dirty = true;
+ break;
+ }
+ }
+
if (dirty || font_dirty || lines_dirty) {
- _shape();
+ if (!_shape()) {
+ // There will be another pass.
+ draw_pending = true;
+ break;
+ }
}
RID ci = get_canvas_item();
@@ -589,6 +622,8 @@ void Label::_notification(int p_what) {
}
ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing;
}
+
+ draw_pending = false;
} break;
case NOTIFICATION_THEME_CHANGED: {
@@ -597,13 +632,7 @@ void Label::_notification(int p_what) {
} break;
case NOTIFICATION_RESIZED: {
- // It may happen that the reshaping due to this size change triggers a cascade of re-layout
- // across the hierarchy where this label belongs to in a way that its size changes multiple
- // times, but ending up with the original size it was already shaped for.
- // This check prevents the catastrophic, freezing infinite cascade of re-layout.
- if (lines_shaped_last_width != get_size().width) {
- lines_dirty = true;
- }
+ lines_dirty = true;
} break;
}
}
@@ -886,7 +915,7 @@ void Label::set_lines_skipped(int p_lines) {
}
lines_skipped = p_lines;
- _update_visible();
+ lines_dirty = true;
queue_redraw();
}
@@ -900,7 +929,7 @@ void Label::set_max_lines_visible(int p_lines) {
}
max_lines_visible = p_lines;
- _update_visible();
+ lines_dirty = true;
queue_redraw();
}
diff --git a/scene/gui/label.h b/scene/gui/label.h
index b80646810b..36b85f7af8 100644
--- a/scene/gui/label.h
+++ b/scene/gui/label.h
@@ -46,11 +46,10 @@ private:
bool clip = false;
TextServer::OverrunBehavior overrun_behavior = TextServer::OVERRUN_NO_TRIMMING;
Size2 minsize;
+ real_t stable_width = -1;
bool uppercase = false;
bool lines_dirty = true;
- int lines_shaped_last_width = -1;
-
bool dirty = true;
bool font_dirty = true;
RID text_rid;
@@ -66,6 +65,7 @@ private:
float visible_ratio = 1.0;
int lines_skipped = 0;
int max_lines_visible = -1;
+ bool draw_pending = false;
Ref<LabelSettings> settings;
@@ -83,8 +83,7 @@ private:
int font_shadow_outline_size;
} theme_cache;
- void _update_visible();
- void _shape();
+ bool _shape();
void _invalidate();
protected:
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index dba08e16cb..a57dccd5c8 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -84,6 +84,7 @@ void LineEdit::_move_caret_left(bool p_select, bool p_move_by_word) {
}
shift_selection_check_post(p_select);
+ _reset_caret_blink_timer();
}
void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
@@ -116,6 +117,7 @@ void LineEdit::_move_caret_right(bool p_select, bool p_move_by_word) {
}
shift_selection_check_post(p_select);
+ _reset_caret_blink_timer();
}
void LineEdit::_move_caret_start(bool p_select) {
@@ -272,6 +274,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
}
grab_focus();
+ accept_event();
return;
}
@@ -381,6 +384,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
}
queue_redraw();
+ return;
}
Ref<InputEventMouseMotion> m = p_event;
@@ -405,6 +409,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
drag_caret_force_displayed = true;
set_caret_at_pixel_pos(m->get_position().x);
}
+
+ return;
}
Ref<InputEventKey> k = p_event;
@@ -458,6 +464,9 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
menu->reset_size();
menu->popup();
menu->grab_focus();
+
+ accept_event();
+ return;
}
}
@@ -467,6 +476,8 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_VIRTUAL_KEYBOARD) && virtual_keyboard_enabled) {
DisplayServer::get_singleton()->virtual_keyboard_hide();
}
+ accept_event();
+ return;
}
if (is_shortcut_keys_enabled()) {
@@ -606,6 +617,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) {
_text_changed();
}
accept_event();
+ return;
}
}
}
diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp
index 027c97b383..dc1d6cc73e 100644
--- a/scene/gui/option_button.cpp
+++ b/scene/gui/option_button.cpp
@@ -491,9 +491,11 @@ void OptionButton::show_popup() {
return;
}
- Size2 button_size = get_global_transform_with_canvas().get_scale() * get_size();
- popup->set_position(get_screen_position() + Size2(0, button_size.height));
- popup->set_size(Size2i(button_size.width, 0));
+ Rect2 rect = get_screen_rect();
+ rect.position.y += rect.size.height;
+ rect.size.height = 0;
+ popup->set_position(rect.position);
+ popup->set_size(rect.size);
// If not triggered by the mouse, start the popup with the checked item (or the first enabled one) focused.
if (current != NONE_SELECTED && !popup->is_item_disabled(current)) {
diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp
index 2ea1b93810..432004dedc 100644
--- a/scene/gui/popup.cpp
+++ b/scene/gui/popup.cpp
@@ -36,7 +36,7 @@
void Popup::_input_from_window(const Ref<InputEvent> &p_event) {
Ref<InputEventKey> key = p_event;
- if (key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
+ if (get_flag(FLAG_POPUP) && key.is_valid() && key->is_pressed() && key->get_keycode() == Key::ESCAPE) {
_close_pressed();
}
}
@@ -102,12 +102,17 @@ void Popup::_notification(int p_what) {
}
} break;
- case NOTIFICATION_WM_CLOSE_REQUEST:
- case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ case NOTIFICATION_WM_CLOSE_REQUEST: {
if (!is_in_edited_scene_root()) {
_close_pressed();
}
} break;
+
+ case NOTIFICATION_APPLICATION_FOCUS_OUT: {
+ if (!is_in_edited_scene_root() && get_flag(FLAG_POPUP)) {
+ _close_pressed();
+ }
+ } break;
}
}
diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp
index ddc11d97b9..1a6adca121 100644
--- a/scene/gui/popup_menu.cpp
+++ b/scene/gui/popup_menu.cpp
@@ -59,6 +59,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const {
for (int i = 0; i < items.size(); i++) {
Size2 item_size;
+ const_cast<PopupMenu *>(this)->_shape_item(i);
Size2 icon_size = items[i].get_icon_size();
item_size.height = _get_item_height(i);
@@ -840,6 +841,9 @@ void PopupMenu::_notification(int p_what) {
float pm_delay = pm->get_submenu_popup_delay();
set_submenu_popup_delay(pm_delay);
}
+ if (!is_embedded()) {
+ set_flag(FLAG_NO_FOCUS, true);
+ }
} break;
case NOTIFICATION_THEME_CHANGED:
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 71ee3c8d0d..973b02b3a3 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1179,7 +1179,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
dot_ul_started = false;
float y_off = TS->shaped_text_get_underline_position(rid);
float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale;
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
}
if (_find_strikethrough(it)) {
if (!st_started) {
@@ -1341,7 +1341,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
dot_ul_started = false;
float y_off = TS->shaped_text_get_underline_position(rid);
float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale;
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
}
if (st_started) {
st_started = false;
@@ -1363,7 +1363,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o
dot_ul_started = false;
float y_off = TS->shaped_text_get_underline_position(rid);
float underline_width = TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale;
- draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, underline_width * 2);
+ draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2));
}
if (st_started) {
st_started = false;
@@ -1797,7 +1797,9 @@ void RichTextLabel::_notification(int p_what) {
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
- queue_redraw();
+ if (is_visible_in_tree()) {
+ queue_redraw();
+ }
} break;
case NOTIFICATION_DRAW: {
@@ -2029,7 +2031,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
}
if (b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) {
- _generate_context_menu();
+ _update_context_menu();
menu->set_position(get_screen_position() + b->get_position());
menu->reset_size();
menu->popup();
@@ -2088,7 +2090,7 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) {
}
if (k->is_action("ui_menu", true)) {
if (context_menu_enabled) {
- _generate_context_menu();
+ _update_context_menu();
menu->set_position(get_screen_position());
menu->reset_size();
menu->popup();
@@ -2665,19 +2667,26 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) {
return false;
}
-void RichTextLabel::_thread_function(void *self) {
- RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self);
- rtl->set_physics_process_internal(true);
- rtl->_process_line_caches();
- rtl->set_physics_process_internal(false);
- rtl->updating.store(false);
- rtl->call_deferred(SNAME("queue_redraw"));
+void RichTextLabel::_thread_function(void *p_userdata) {
+ _process_line_caches();
+ updating.store(false);
+ call_deferred(SNAME("thread_end"));
+}
+
+void RichTextLabel::_thread_end() {
+ set_physics_process_internal(false);
+ if (is_visible_in_tree()) {
+ queue_redraw();
+ }
}
void RichTextLabel::_stop_thread() {
if (threaded) {
stop_thread.store(true);
- thread.wait_to_finish();
+ if (task != WorkerThreadPool::INVALID_TASK_ID) {
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(task);
+ task = WorkerThreadPool::INVALID_TASK_ID;
+ }
}
}
@@ -2787,7 +2796,8 @@ bool RichTextLabel::_validate_line_caches() {
if (threaded) {
updating.store(true);
loaded.store(true);
- thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this));
+ task = WorkerThreadPool::get_singleton()->add_template_task(this, &RichTextLabel::_thread_function, nullptr, true, vformat("RichTextLabelShape:%x", (int64_t)get_instance_id()));
+ set_physics_process_internal(true);
loading_started = OS::get_singleton()->get_ticks_msec();
return false;
} else {
@@ -4982,7 +4992,9 @@ bool RichTextLabel::is_shortcut_keys_enabled() const {
// Context menu.
PopupMenu *RichTextLabel::get_menu() const {
- const_cast<RichTextLabel *>(this)->_generate_context_menu();
+ if (!menu) {
+ const_cast<RichTextLabel *>(this)->_generate_context_menu();
+ }
return menu;
}
@@ -5456,6 +5468,9 @@ void RichTextLabel::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu);
ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible);
+ ClassDB::bind_method(D_METHOD("menu_option", "option"), &RichTextLabel::menu_option);
+
+ ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end);
// Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets.
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode");
@@ -5505,33 +5520,9 @@ void RichTextLabel::_bind_methods() {
BIND_ENUM_CONSTANT(LIST_ROMAN);
BIND_ENUM_CONSTANT(LIST_DOTS);
- BIND_ENUM_CONSTANT(ITEM_FRAME);
- BIND_ENUM_CONSTANT(ITEM_TEXT);
- BIND_ENUM_CONSTANT(ITEM_IMAGE);
- BIND_ENUM_CONSTANT(ITEM_NEWLINE);
- BIND_ENUM_CONSTANT(ITEM_FONT);
- BIND_ENUM_CONSTANT(ITEM_FONT_SIZE);
- BIND_ENUM_CONSTANT(ITEM_FONT_FEATURES);
- BIND_ENUM_CONSTANT(ITEM_COLOR);
- BIND_ENUM_CONSTANT(ITEM_OUTLINE_SIZE);
- BIND_ENUM_CONSTANT(ITEM_OUTLINE_COLOR);
- BIND_ENUM_CONSTANT(ITEM_UNDERLINE);
- BIND_ENUM_CONSTANT(ITEM_STRIKETHROUGH);
- BIND_ENUM_CONSTANT(ITEM_PARAGRAPH);
- BIND_ENUM_CONSTANT(ITEM_INDENT);
- BIND_ENUM_CONSTANT(ITEM_LIST);
- BIND_ENUM_CONSTANT(ITEM_TABLE);
- BIND_ENUM_CONSTANT(ITEM_FADE);
- BIND_ENUM_CONSTANT(ITEM_SHAKE);
- BIND_ENUM_CONSTANT(ITEM_WAVE);
- BIND_ENUM_CONSTANT(ITEM_TORNADO);
- BIND_ENUM_CONSTANT(ITEM_RAINBOW);
- BIND_ENUM_CONSTANT(ITEM_BGCOLOR);
- BIND_ENUM_CONSTANT(ITEM_FGCOLOR);
- BIND_ENUM_CONSTANT(ITEM_META);
- BIND_ENUM_CONSTANT(ITEM_HINT);
- BIND_ENUM_CONSTANT(ITEM_DROPCAP);
- BIND_ENUM_CONSTANT(ITEM_CUSTOMFX);
+ BIND_ENUM_CONSTANT(MENU_COPY);
+ BIND_ENUM_CONSTANT(MENU_SELECT_ALL);
+ BIND_ENUM_CONSTANT(MENU_MAX);
}
TextServer::VisibleCharactersBehavior RichTextLabel::get_visible_characters_behavior() const {
@@ -5663,19 +5654,32 @@ Size2 RichTextLabel::get_minimum_size() const {
// Context menu.
void RichTextLabel::_generate_context_menu() {
- if (!menu) {
- menu = memnew(PopupMenu);
- add_child(menu, false, INTERNAL_MODE_FRONT);
+ menu = memnew(PopupMenu);
+ add_child(menu, false, INTERNAL_MODE_FRONT);
+ menu->connect("id_pressed", callable_mp(this, &RichTextLabel::menu_option));
+
+ menu->add_item(RTR("Copy"), MENU_COPY);
+ menu->add_item(RTR("Select All"), MENU_SELECT_ALL);
+}
- menu->connect("id_pressed", callable_mp(this, &RichTextLabel::_menu_option));
+void RichTextLabel::_update_context_menu() {
+ if (!menu) {
+ _generate_context_menu();
}
- // Reorganize context menu.
- menu->clear();
- if (selection.enabled) {
- menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE);
- menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE);
+ int idx = -1;
+
+#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \
+ idx = m_menu->get_item_index(m_id); \
+ if (idx >= 0) { \
+ m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \
+ m_menu->set_item_disabled(idx, m_disabled); \
}
+
+ MENU_ITEM_ACTION_DISABLED(menu, MENU_COPY, "ui_copy", !selection.enabled)
+ MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selection.enabled)
+
+#undef MENU_ITEM_ACTION_DISABLED
}
Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
@@ -5703,7 +5707,7 @@ Key RichTextLabel::_get_menu_action_accelerator(const String &p_action) {
}
}
-void RichTextLabel::_menu_option(int p_option) {
+void RichTextLabel::menu_option(int p_option) {
switch (p_option) {
case MENU_COPY: {
selection_copy();
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 58b82d4672..1dae8b75ca 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -31,6 +31,7 @@
#ifndef RICH_TEXT_LABEL_H
#define RICH_TEXT_LABEL_H
+#include "core/object/worker_thread_pool.h"
#include "rich_text_effect.h"
#include "scene/gui/popup_menu.h"
#include "scene/gui/scroll_bar.h"
@@ -80,6 +81,7 @@ public:
enum MenuItems {
MENU_COPY,
MENU_SELECT_ALL,
+ MENU_MAX
};
enum DefaultFont {
@@ -369,7 +371,7 @@ private:
Item *current = nullptr;
ItemFrame *current_frame = nullptr;
- Thread thread;
+ WorkerThreadPool::TaskID task = WorkerThreadPool::INVALID_TASK_ID;
Mutex data_mutex;
bool threaded = false;
std::atomic<bool> stop_thread;
@@ -409,7 +411,8 @@ private:
void _invalidate_current_line(ItemFrame *p_frame);
- static void _thread_function(void *self);
+ void _thread_function(void *p_userdata);
+ void _thread_end();
void _stop_thread();
bool _validate_line_caches();
void _process_line_caches();
@@ -452,8 +455,8 @@ private:
// Context menu.
PopupMenu *menu = nullptr;
void _generate_context_menu();
+ void _update_context_menu();
Key _get_menu_action_accelerator(const String &p_action);
- void _menu_option(int p_option);
int visible_characters = -1;
float visible_ratio = 1.0;
@@ -686,6 +689,7 @@ public:
// Context menu.
PopupMenu *get_menu() const;
bool is_menu_visible() const;
+ void menu_option(int p_option);
void parse_bbcode(const String &p_bbcode);
void append_text(const String &p_bbcode);
@@ -736,6 +740,6 @@ public:
};
VARIANT_ENUM_CAST(RichTextLabel::ListType);
-VARIANT_ENUM_CAST(RichTextLabel::ItemType);
+VARIANT_ENUM_CAST(RichTextLabel::MenuItems);
#endif // RICH_TEXT_LABEL_H
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index ead9550b93..0c0125df76 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -115,7 +115,7 @@ void SplitContainerDragger::_notification(int p_what) {
return;
}
- Ref<Texture2D> tex = sc->get_theme_icon(SNAME("grabber"));
+ Ref<Texture2D> tex = sc->_get_grabber_icon();
draw_texture(tex, (get_size() - tex->get_size()) / 2);
} break;
}
diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp
index 7c1d2f95a9..f10e1c2cd1 100644
--- a/scene/gui/subviewport_container.cpp
+++ b/scene/gui/subviewport_container.cpp
@@ -85,7 +85,7 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) {
continue;
}
- c->set_size(get_size() / shrink);
+ c->set_size_force(get_size() / shrink);
}
queue_redraw();
@@ -116,7 +116,7 @@ void SubViewportContainer::_notification(int p_what) {
continue;
}
- c->set_size(get_size() / shrink);
+ c->set_size_force(get_size() / shrink);
}
} break;
diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp
index eca6cb3eef..5e378a0321 100644
--- a/scene/gui/tab_bar.cpp
+++ b/scene/gui/tab_bar.cpp
@@ -342,6 +342,8 @@ void TabBar::_notification(int p_what) {
_shape(i);
}
+ queue_redraw();
+
[[fallthrough]];
}
case NOTIFICATION_RESIZED: {
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index d785280701..2b3b577697 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -4362,7 +4362,9 @@ int TextEdit::get_minimap_line_at_pos(const Point2i &p_pos) const {
if (first_vis_line > 0 && minimap_line >= 0) {
minimap_line -= get_next_visible_line_index_offset_from(first_vis_line, 0, -num_lines_before).x;
minimap_line -= (minimap_line > 0 && smooth_scroll_enabled ? 1 : 0);
- } else {
+ }
+
+ if (minimap_line < 0) {
minimap_line = 0;
}
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index d9e6157489..f8c2e9f4ad 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1775,10 +1775,10 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
RID ci = get_canvas_item();
- if (rtl) {
+ if (rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(MAX(0, rect.size.width));
+ p_cell.text_buf->set_width(rect.size.width);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -1800,10 +1800,10 @@ void Tree::draw_item_rect(TreeItem::Cell &p_cell, const Rect2i &p_rect, const Co
rect.size.x -= bmsize.x + theme_cache.h_separation;
}
- if (!rtl) {
+ if (!rtl && rect.size.width > 0) {
Point2 draw_pos = rect.position;
draw_pos.y += Math::floor((rect.size.y - p_cell.text_buf->get_size().y) / 2.0);
- p_cell.text_buf->set_width(MAX(0, rect.size.width));
+ p_cell.text_buf->set_width(rect.size.width);
if (p_ol_size > 0 && p_ol_color.a > 0) {
p_cell.text_buf->draw_outline(ci, draw_pos, p_ol_size, p_ol_color);
}
@@ -5256,7 +5256,6 @@ void Tree::_bind_methods() {
ADD_SIGNAL(MethodInfo("empty_clicked", PropertyInfo(Variant::VECTOR2, "position"), PropertyInfo(Variant::INT, "mouse_button_index")));
ADD_SIGNAL(MethodInfo("item_edited"));
ADD_SIGNAL(MethodInfo("custom_item_clicked", PropertyInfo(Variant::INT, "mouse_button_index")));
- ADD_SIGNAL(MethodInfo("item_custom_button_pressed"));
ADD_SIGNAL(MethodInfo("item_icon_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")));
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index 6eb25bf852..1f3bbff779 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -236,7 +236,6 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) {
AudioServer::get_singleton()->unlock();
if (!playback.is_null()) {
- playback->set_loop(loops);
playback->set_paused(paused);
texture = playback->get_texture();
@@ -344,6 +343,12 @@ int VideoStreamPlayer::get_buffering_msec() const {
void VideoStreamPlayer::set_audio_track(int p_track) {
audio_track = p_track;
+ if (stream.is_valid()) {
+ stream->set_audio_track(audio_track);
+ }
+ if (playback.is_valid()) {
+ playback->set_audio_track(audio_track);
+ }
}
int VideoStreamPlayer::get_audio_track() const {
diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h
index 09ef272a9a..1fd599a9e1 100644
--- a/scene/gui/video_stream_player.h
+++ b/scene/gui/video_stream_player.h
@@ -65,7 +65,6 @@ class VideoStreamPlayer : public Control {
float volume = 1.0;
double last_audio_time = 0.0;
bool expand = false;
- bool loops = false;
int buffering_ms = 500;
int audio_track = 0;
int bus_index = 0;
diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp
index 145497fa61..51af886709 100644
--- a/scene/gui/view_panner.cpp
+++ b/scene/gui/view_panner.cpp
@@ -125,7 +125,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect)
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
- callback_helper(pan_callback, varray(-pan_gesture->get_delta(), p_event));
+ callback_helper(pan_callback, varray(-pan_gesture->get_delta() * scroll_speed, p_event));
}
Ref<InputEventScreenDrag> screen_drag = p_event;