diff options
Diffstat (limited to 'scene/gui')
41 files changed, 368 insertions, 223 deletions
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index 711dd13781..1663a7d602 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -35,16 +35,10 @@ Size2 AspectRatioContainer::get_minimum_size() const { Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } - if (!c->is_visible()) { - continue; - } Size2 minsize = c->get_combined_minimum_size(); ms = ms.max(minsize); } @@ -107,13 +101,10 @@ void AspectRatioContainer::_notification(int p_what) { bool rtl = is_layout_rtl(); Size2 size = get_size(); for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } // Temporary fix for editor crash. TextureRect *trect = Object::cast_to<TextureRect>(c); diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 2728126e64..d8fcbbb883 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -55,11 +55,8 @@ void BoxContainer::_resort() { HashMap<Control *, _MinSizeCache> min_size_cache; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } @@ -109,11 +106,8 @@ void BoxContainer::_resort() { float error = 0.0; // Keep track of accumulated error in pixels for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } @@ -201,11 +195,8 @@ void BoxContainer::_resort() { } for (int i = start; i != end; i += delta) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } @@ -253,14 +244,7 @@ Size2 BoxContainer::get_minimum_size() const { 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()) { + if (!c || !c->is_visible() || c->is_set_as_top_level()) { continue; } diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index acdeae289b..1af33d2814 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -36,16 +36,10 @@ Size2 CenterContainer::get_minimum_size() const { } Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } - if (!c->is_visible()) { - continue; - } Size2 minsize = c->get_combined_minimum_size(); ms = ms.max(minsize); } @@ -81,14 +75,10 @@ void CenterContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { Size2 size = get_size(); for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } - Size2 minsize = c->get_combined_minimum_size(); Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor(); fit_child_in_rect(c, Rect2(ofs, minsize)); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 848a598ebb..aeaaac1efc 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -458,8 +458,8 @@ void ColorPicker::set_editor_settings(Object *p_editor_settings) { } } - for (int i = 0; i < preset_cache.size(); i++) { - presets.push_back(preset_cache[i]); + for (const Color &preset : preset_cache) { + presets.push_back(preset); } if (recent_preset_cache.is_empty()) { @@ -469,8 +469,8 @@ void ColorPicker::set_editor_settings(Object *p_editor_settings) { } } - for (int i = 0; i < recent_preset_cache.size(); i++) { - recent_presets.push_back(recent_preset_cache[i]); + for (const Color &preset : recent_preset_cache) { + recent_presets.push_back(preset); } _update_presets(); @@ -660,8 +660,8 @@ void ColorPicker::_update_presets() { for (int i = 1; i < preset_container->get_child_count(); i++) { preset_container->get_child(i)->queue_free(); } - for (int i = 0; i < preset_cache.size(); i++) { - _add_preset_button(preset_size, preset_cache[i]); + for (const Color &preset : preset_cache) { + _add_preset_button(preset_size, preset); } _notification(NOTIFICATION_VISIBILITY_CHANGED); } @@ -677,13 +677,13 @@ void ColorPicker::_update_recent_presets() { } recent_presets.clear(); - for (int i = 0; i < recent_preset_cache.size(); i++) { - recent_presets.push_back(recent_preset_cache[i]); + for (const Color &preset : recent_preset_cache) { + recent_presets.push_back(preset); } int preset_size = _get_preset_size(); - for (int i = 0; i < recent_presets.size(); i++) { - _add_recent_preset_button(preset_size, recent_presets[i]); + for (const Color &preset : recent_presets) { + _add_recent_preset_button(preset_size, preset); } _notification(NOTIFICATION_VISIBILITY_CHANGED); @@ -937,8 +937,9 @@ void ColorPicker::erase_recent_preset(const Color &p_color) { PackedColorArray ColorPicker::get_presets() const { PackedColorArray arr; arr.resize(presets.size()); - for (int i = 0; i < presets.size(); i++) { - arr.set(i, presets[i]); + int i = 0; + for (List<Color>::ConstIterator itr = presets.begin(); itr != presets.end(); ++itr, ++i) { + arr.set(i, *itr); } return arr; } @@ -946,8 +947,9 @@ PackedColorArray ColorPicker::get_presets() const { PackedColorArray ColorPicker::get_recent_presets() const { PackedColorArray arr; arr.resize(recent_presets.size()); - for (int i = 0; i < recent_presets.size(); i++) { - arr.set(i, recent_presets[i]); + int i = 0; + for (List<Color>::ConstIterator itr = recent_presets.begin(); itr != recent_presets.end(); ++itr, ++i) { + arr.set(i, *itr); } return arr; } diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index c6e66c95c6..625964c5e3 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -86,10 +86,10 @@ void Container::_sort_children() { } notification(NOTIFICATION_PRE_SORT_CHILDREN); - emit_signal(SceneStringNames::get_singleton()->pre_sort_children); + emit_signal(SceneStringName(pre_sort_children)); notification(NOTIFICATION_SORT_CHILDREN); - emit_signal(SceneStringNames::get_singleton()->sort_children); + emit_signal(SceneStringName(sort_children)); pending_sort = false; } @@ -141,6 +141,14 @@ void Container::queue_sort() { pending_sort = true; } +Control *Container::as_sortable_control(Node *p_node) const { + Control *c = Object::cast_to<Control>(p_node); + if (!c || !c->is_visible_in_tree() || c->is_set_as_top_level()) { + return nullptr; + } + return c; +} + Vector<int> Container::get_allowed_size_flags_horizontal() const { Vector<int> flags; if (GDVIRTUAL_CALL(_get_allowed_size_flags_horizontal, flags)) { diff --git a/scene/gui/container.h b/scene/gui/container.h index 94c3c540d7..405220cee6 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -42,6 +42,8 @@ class Container : public Control { protected: void queue_sort(); + Control *as_sortable_control(Node *p_node) const; + virtual void add_child_notify(Node *p_child) override; virtual void move_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d430fe9bfc..9c27f46467 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -142,8 +142,8 @@ Size2 Control::_edit_get_scale() const { void Control::_edit_set_rect(const Rect2 &p_edit_rect) { ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); - set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); - set_size(p_edit_rect.size.snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snappedf(1), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); + set_size(p_edit_rect.size.snappedf(1), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); } Rect2 Control::_edit_get_rect() const { @@ -208,28 +208,42 @@ void Control::set_root_layout_direction(int p_root_dir) { #ifdef TOOLS_ENABLED void Control::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { ERR_READ_THREAD_GUARD; - CanvasItem::get_argument_options(p_function, p_idx, r_options); - if (p_idx == 0) { - List<StringName> sn; const String pf = p_function; + Theme::DataType type = Theme::DATA_TYPE_MAX; + if (pf == "add_theme_color_override" || pf == "has_theme_color" || pf == "has_theme_color_override" || pf == "get_theme_color") { - ThemeDB::get_singleton()->get_default_theme()->get_color_list(get_class(), &sn); - } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { - ThemeDB::get_singleton()->get_default_theme()->get_stylebox_list(get_class(), &sn); + type = Theme::DATA_TYPE_COLOR; + } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { + type = Theme::DATA_TYPE_CONSTANT; } else if (pf == "add_theme_font_override" || pf == "has_theme_font" || pf == "has_theme_font_override" || pf == "get_theme_font") { - ThemeDB::get_singleton()->get_default_theme()->get_font_list(get_class(), &sn); + type = Theme::DATA_TYPE_FONT; } else if (pf == "add_theme_font_size_override" || pf == "has_theme_font_size" || pf == "has_theme_font_size_override" || pf == "get_theme_font_size") { - ThemeDB::get_singleton()->get_default_theme()->get_font_size_list(get_class(), &sn); - } else if (pf == "add_theme_constant_override" || pf == "has_theme_constant" || pf == "has_theme_constant_override" || pf == "get_theme_constant") { - ThemeDB::get_singleton()->get_default_theme()->get_constant_list(get_class(), &sn); + type = Theme::DATA_TYPE_FONT_SIZE; + } else if (pf == "add_theme_icon_override" || pf == "has_theme_icon" || pf == "has_theme_icon_override" || pf == "get_theme_icon") { + type = Theme::DATA_TYPE_ICON; + } else if (pf == "add_theme_style_override" || pf == "has_theme_style" || pf == "has_theme_style_override" || pf == "get_theme_style") { + type = Theme::DATA_TYPE_STYLEBOX; } - sn.sort_custom<StringName::AlphCompare>(); - for (const StringName &name : sn) { - r_options->push_back(String(name).quote()); + if (type != Theme::DATA_TYPE_MAX) { + List<ThemeDB::ThemeItemBind> theme_items; + ThemeDB::get_singleton()->get_class_items(get_class_name(), &theme_items, true, type); + + List<StringName> sn; + for (const ThemeDB::ThemeItemBind &E : theme_items) { + if (E.data_type == type) { + sn.push_back(E.item_name); + } + } + + sn.sort_custom<StringName::AlphCompare>(); + for (const StringName &name : sn) { + r_options->push_back(String(name).quote()); + } } } + CanvasItem::get_argument_options(p_function, p_idx, r_options); } #endif @@ -1383,6 +1397,15 @@ void Control::_set_position(const Point2 &p_point) { void Control::set_position(const Point2 &p_point, bool p_keep_offsets) { ERR_MAIN_THREAD_GUARD; + +#ifdef TOOLS_ENABLED + // Can't compute anchors, set position directly and return immediately. + if (saving && !is_inside_tree()) { + data.pos_cache = p_point; + return; + } +#endif + if (p_keep_offsets) { _compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor); } else { @@ -1441,6 +1464,14 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { new_size.y = min.y; } +#ifdef TOOLS_ENABLED + // Can't compute anchors, set size directly and return immediately. + if (saving && !is_inside_tree()) { + data.size_cache = new_size; + return; + } +#endif + if (p_keep_offsets) { _compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor); } else { @@ -1575,7 +1606,7 @@ void Control::_update_minimum_size() { if (minsize != data.last_minimum_size) { data.last_minimum_size = minsize; _size_changed(); - emit_signal(SceneStringNames::get_singleton()->minimum_size_changed); + emit_signal(SceneStringName(minimum_size_changed)); } } @@ -1739,7 +1770,7 @@ void Control::set_h_size_flags(BitField<SizeFlags> p_flags) { return; } data.h_size_flags = p_flags; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); + emit_signal(SceneStringName(size_flags_changed)); } BitField<Control::SizeFlags> Control::get_h_size_flags() const { @@ -1753,7 +1784,7 @@ void Control::set_v_size_flags(BitField<SizeFlags> p_flags) { return; } data.v_size_flags = p_flags; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); + emit_signal(SceneStringName(size_flags_changed)); } BitField<Control::SizeFlags> Control::get_v_size_flags() const { @@ -1768,7 +1799,7 @@ void Control::set_stretch_ratio(real_t p_ratio) { } data.expand = p_ratio; - emit_signal(SceneStringNames::get_singleton()->size_flags_changed); + emit_signal(SceneStringName(size_flags_changed)); } real_t Control::get_stretch_ratio() const { @@ -1780,7 +1811,7 @@ real_t Control::get_stretch_ratio() const { void Control::_call_gui_input(const Ref<InputEvent> &p_event) { if (p_event->get_device() != InputEvent::DEVICE_ID_INTERNAL) { - emit_signal(SceneStringNames::get_singleton()->gui_input, p_event); // Signal should be first, so it's possible to override an event (and then accept it). + emit_signal(SceneStringName(gui_input), p_event); // Signal should be first, so it's possible to override an event (and then accept it). } if (!is_inside_tree() || get_viewport()->is_input_handled()) { return; // Input was handled, abort. @@ -3140,6 +3171,14 @@ Control *Control::make_custom_tooltip(const String &p_text) const { void Control::_notification(int p_notification) { ERR_MAIN_THREAD_GUARD; switch (p_notification) { +#ifdef TOOLS_ENABLED + case NOTIFICATION_EDITOR_PRE_SAVE: { + saving = true; + } break; + case NOTIFICATION_EDITOR_POST_SAVE: { + saving = false; + } break; +#endif case NOTIFICATION_POSTINITIALIZE: { data.initialized = true; @@ -3260,7 +3299,7 @@ void Control::_notification(int p_notification) { } break; case NOTIFICATION_RESIZED: { - emit_signal(SceneStringNames::get_singleton()->resized); + emit_signal(SceneStringName(resized)); } break; case NOTIFICATION_DRAW: { @@ -3270,25 +3309,25 @@ void Control::_notification(int p_notification) { } break; case NOTIFICATION_MOUSE_ENTER: { - emit_signal(SceneStringNames::get_singleton()->mouse_entered); + emit_signal(SceneStringName(mouse_entered)); } break; case NOTIFICATION_MOUSE_EXIT: { - emit_signal(SceneStringNames::get_singleton()->mouse_exited); + emit_signal(SceneStringName(mouse_exited)); } break; case NOTIFICATION_FOCUS_ENTER: { - emit_signal(SceneStringNames::get_singleton()->focus_entered); + emit_signal(SceneStringName(focus_entered)); queue_redraw(); } break; case NOTIFICATION_FOCUS_EXIT: { - emit_signal(SceneStringNames::get_singleton()->focus_exited); + emit_signal(SceneStringName(focus_exited)); queue_redraw(); } break; case NOTIFICATION_THEME_CHANGED: { - emit_signal(SceneStringNames::get_singleton()->theme_changed); + emit_signal(SceneStringName(theme_changed)); _invalidate_theme_cache(); _update_theme_item_cache(); diff --git a/scene/gui/control.h b/scene/gui/control.h index bdb908e089..c784d4330d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -47,6 +47,10 @@ class ThemeContext; class Control : public CanvasItem { GDCLASS(Control, CanvasItem); +#ifdef TOOLS_ENABLED + bool saving = false; +#endif + public: enum Anchor { ANCHOR_BEGIN = 0, diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 97a2917dc1..91424c0ffd 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -1344,6 +1344,7 @@ void FileDialog::_bind_methods() { Option defaults; base_property_helper.set_prefix("option_"); + base_property_helper.set_array_length_getter(&FileDialog::get_option_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "name"), defaults.name, &FileDialog::set_option_name, &FileDialog::get_option_name); base_property_helper.register_property(PropertyInfo(Variant::PACKED_STRING_ARRAY, "values"), defaults.values, &FileDialog::set_option_values, &FileDialog::get_option_values); base_property_helper.register_property(PropertyInfo(Variant::INT, "default"), defaults.default_idx, &FileDialog::set_option_default, &FileDialog::get_option_default); diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index 9f79da2905..ceffd9d103 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -38,6 +38,7 @@ struct _LineData { int min_line_length = 0; int stretch_avail = 0; float stretch_ratio_total = 0; + bool is_filled = false; }; void FlowContainer::_resort() { @@ -58,14 +59,12 @@ void FlowContainer::_resort() { 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; + Control *last_child = nullptr; // 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()) { - continue; - } - if (child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i)); + if (!child) { continue; } @@ -77,7 +76,7 @@ void FlowContainer::_resort() { } if (ofs.y + child_msc.y > current_container_size) { line_length = ofs.y - theme_cache.v_separation; - lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, true }); // Move in new column (vertical line). ofs.x += line_height + theme_cache.h_separation; @@ -99,7 +98,7 @@ void FlowContainer::_resort() { } if (ofs.x + child_msc.x > current_container_size) { line_length = ofs.x - theme_cache.h_separation; - lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total }); + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, true }); // Move in new line. ofs.y += line_height + theme_cache.v_separation; @@ -116,11 +115,16 @@ void FlowContainer::_resort() { ofs.x += child_msc.x; } + last_child = child; 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 }); + bool is_filled = false; + if (last_child != nullptr) { + is_filled = vertical ? (ofs.y + last_child->get_combined_minimum_size().y > current_container_size ? true : false) : (ofs.x + last_child->get_combined_minimum_size().x > current_container_size ? true : false); + } + lines_data.push_back(_LineData{ children_in_current_line, line_height, line_length, current_container_size - line_length, line_stretch_ratio_total, is_filled }); // Second pass for in-line expansion and alignment. @@ -131,11 +135,8 @@ void FlowContainer::_resort() { 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()) { - continue; - } - if (child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i)); + if (!child) { continue; } Size2i child_size = children_minsize_cache[child]; @@ -158,17 +159,43 @@ void FlowContainer::_resort() { // but only if the line doesn't contain a child that expands. if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) { int alignment_ofs = 0; + bool is_not_first_line_and_not_filled = current_line_idx != 0 && !line_data.is_filled; + float prior_stretch_avail = is_not_first_line_and_not_filled ? lines_data[current_line_idx - 1].stretch_avail : 0.0; switch (alignment) { - case ALIGNMENT_CENTER: - alignment_ofs = line_data.stretch_avail / 2; - break; - case ALIGNMENT_END: - alignment_ofs = line_data.stretch_avail; - break; + case ALIGNMENT_BEGIN: { + if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && is_not_first_line_and_not_filled) { + if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_END) { + alignment_ofs = line_data.stretch_avail - prior_stretch_avail; + } else if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_CENTER) { + alignment_ofs = (line_data.stretch_avail - prior_stretch_avail) * 0.5; + } + } + } break; + case ALIGNMENT_CENTER: { + if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && last_wrap_alignment != LAST_WRAP_ALIGNMENT_CENTER && is_not_first_line_and_not_filled) { + if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_END) { + alignment_ofs = line_data.stretch_avail - (prior_stretch_avail * 0.5); + } else { // Is LAST_WRAP_ALIGNMENT_BEGIN + alignment_ofs = prior_stretch_avail * 0.5; + } + } else { + alignment_ofs = line_data.stretch_avail * 0.5; + } + } break; + case ALIGNMENT_END: { + if (last_wrap_alignment != LAST_WRAP_ALIGNMENT_INHERIT && last_wrap_alignment != LAST_WRAP_ALIGNMENT_END && is_not_first_line_and_not_filled) { + if (last_wrap_alignment == LAST_WRAP_ALIGNMENT_BEGIN) { + alignment_ofs = prior_stretch_avail; + } else { // Is LAST_WRAP_ALIGNMENT_CENTER + alignment_ofs = prior_stretch_avail + (line_data.stretch_avail - prior_stretch_avail) * 0.5; + } + } else { + alignment_ofs = line_data.stretch_avail; + } + } break; default: break; } - if (vertical) { /* VERTICAL */ ofs.y += alignment_ofs; } else { /* HORIZONTAL */ @@ -223,17 +250,10 @@ 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)); + Control *c = as_sortable_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(); @@ -314,6 +334,18 @@ FlowContainer::AlignmentMode FlowContainer::get_alignment() const { return alignment; } +void FlowContainer::set_last_wrap_alignment(LastWrapAlignmentMode p_last_wrap_alignment) { + if (last_wrap_alignment == p_last_wrap_alignment) { + return; + } + last_wrap_alignment = p_last_wrap_alignment; + _resort(); +} + +FlowContainer::LastWrapAlignmentMode FlowContainer::get_last_wrap_alignment() const { + return last_wrap_alignment; +} + void FlowContainer::set_vertical(bool p_vertical) { ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); vertical = p_vertical; @@ -346,6 +378,8 @@ void FlowContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment); ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment); + ClassDB::bind_method(D_METHOD("set_last_wrap_alignment", "last_wrap_alignment"), &FlowContainer::set_last_wrap_alignment); + ClassDB::bind_method(D_METHOD("get_last_wrap_alignment"), &FlowContainer::get_last_wrap_alignment); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); ClassDB::bind_method(D_METHOD("set_reverse_fill", "reverse_fill"), &FlowContainer::set_reverse_fill); @@ -354,8 +388,13 @@ void FlowContainer::_bind_methods() { BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); BIND_ENUM_CONSTANT(ALIGNMENT_END); + BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_INHERIT); + BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_BEGIN); + BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_CENTER); + BIND_ENUM_CONSTANT(LAST_WRAP_ALIGNMENT_END); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "last_wrap_alignment", PROPERTY_HINT_ENUM, "Inherit,Begin,Center,End"), "set_last_wrap_alignment", "get_last_wrap_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverse_fill"), "set_reverse_fill", "is_reverse_fill"); diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index 90da73aaab..65ebc89c78 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -42,6 +42,12 @@ public: ALIGNMENT_CENTER, ALIGNMENT_END }; + enum LastWrapAlignmentMode { + LAST_WRAP_ALIGNMENT_INHERIT, + LAST_WRAP_ALIGNMENT_BEGIN, + LAST_WRAP_ALIGNMENT_CENTER, + LAST_WRAP_ALIGNMENT_END + }; private: int cached_size = 0; @@ -50,6 +56,7 @@ private: bool vertical = false; bool reverse_fill = false; AlignmentMode alignment = ALIGNMENT_BEGIN; + LastWrapAlignmentMode last_wrap_alignment = LAST_WRAP_ALIGNMENT_INHERIT; struct ThemeCache { int h_separation = 0; @@ -71,6 +78,9 @@ public: void set_alignment(AlignmentMode p_alignment); AlignmentMode get_alignment() const; + void set_last_wrap_alignment(LastWrapAlignmentMode p_last_wrap_alignment); + LastWrapAlignmentMode get_last_wrap_alignment() const; + void set_vertical(bool p_vertical); bool is_vertical() const; @@ -102,5 +112,6 @@ public: }; VARIANT_ENUM_CAST(FlowContainer::AlignmentMode); +VARIANT_ENUM_CAST(FlowContainer::LastWrapAlignmentMode); #endif // FLOW_CONTAINER_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index ef9c7e35ed..35d7146d20 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -501,7 +501,7 @@ void GraphEdit::_graph_element_resize_request(const Vector2 &p_new_minsize, Node // Snap the new size to the grid if snapping is enabled. Vector2 new_size = p_new_minsize; if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) { - new_size = new_size.snapped(Vector2(snapping_distance, snapping_distance)); + new_size = new_size.snappedf(snapping_distance); } // Disallow resizing the frame to a size smaller than the minimum size of the attached nodes. @@ -851,7 +851,7 @@ void GraphEdit::_set_position_of_frame_attached_nodes(GraphFrame *p_frame, const Vector2 pos = (attached_node->get_drag_from() * zoom + drag_accum) / zoom; if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CTRL)) { - pos = pos.snapped(Vector2(snapping_distance, snapping_distance)); + pos = pos.snappedf(snapping_distance); } // Recursively move graph frames. @@ -1059,7 +1059,7 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) { port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); int type = graph_node->get_output_port_type(j); - if ((type == connecting_type || + if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(type, connecting_type))) && is_in_output_hotzone(graph_node, j, mpos, port_size)) { if (!is_node_hover_valid(graph_node->get_name(), j, connecting_from_node, connecting_from_port_index)) { @@ -1084,7 +1084,7 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) { port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); int type = graph_node->get_input_port_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnectionType(connecting_type, type))) && + if ((type == connecting_type || graph_node->is_ignoring_valid_connection_type() || valid_connection_types.has(ConnectionType(connecting_type, type))) && is_in_input_hotzone(graph_node, j, mpos, port_size)) { if (!is_node_hover_valid(connecting_from_node, connecting_from_port_index, graph_node->get_name(), j)) { continue; @@ -1117,6 +1117,8 @@ void GraphEdit::_top_connection_layer_input(const Ref<InputEvent> &p_ev) { emit_signal(SNAME("connection_from_empty"), connecting_from_node, connecting_from_port_index, mb->get_position()); } } + } else { + set_selected(get_node_or_null(NodePath(connecting_from_node))); } if (connecting) { @@ -1636,12 +1638,12 @@ void GraphEdit::_draw_grid() { void GraphEdit::set_selected(Node *p_child) { for (int i = get_child_count() - 1; i >= 0; i--) { - GraphNode *graph_node = Object::cast_to<GraphNode>(get_child(i)); - if (!graph_node) { + GraphElement *graph_element = Object::cast_to<GraphElement>(get_child(i)); + if (!graph_element) { continue; } - graph_node->set_selected(graph_node == p_child); + graph_element->set_selected(graph_element == p_child); } } @@ -1678,7 +1680,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { // Snapping can be toggled temporarily by holding down Ctrl. // This is done here as to not toggle the grid when holding down Ctrl. if (snapping_enabled ^ Input::get_singleton()->is_key_pressed(Key::CMD_OR_CTRL)) { - pos = pos.snapped(Vector2(snapping_distance, snapping_distance)); + pos = pos.snappedf(snapping_distance); } graph_element->set_position_offset(pos); diff --git a/scene/gui/graph_edit_arranger.cpp b/scene/gui/graph_edit_arranger.cpp index 49998beb42..fa1059c667 100644 --- a/scene/gui/graph_edit_arranger.cpp +++ b/scene/gui/graph_edit_arranger.cpp @@ -180,7 +180,7 @@ void GraphEditArranger::arrange_nodes() { if (graph_edit->is_snapping_enabled()) { float snapping_distance = graph_edit->get_snapping_distance(); - pos = pos.snapped(Vector2(snapping_distance, snapping_distance)); + pos = pos.snappedf(snapping_distance); } graph_node->set_position_offset(pos); graph_node->set_drag(false); diff --git a/scene/gui/graph_element.cpp b/scene/gui/graph_element.cpp index 9c13a3ad9e..e231b05d7f 100644 --- a/scene/gui/graph_element.cpp +++ b/scene/gui/graph_element.cpp @@ -49,14 +49,10 @@ void GraphElement::_resort() { Size2 size = get_size(); 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()) { + Control *child = as_sortable_control(get_child(i)); + if (!child) { continue; } - fit_child_in_rect(child, Rect2(Point2(), size)); } } @@ -65,10 +61,7 @@ Size2 GraphElement::get_minimum_size() const { Size2 minsize; for (int i = 0; i < get_child_count(); i++) { Control *child = Object::cast_to<Control>(get_child(i)); - if (!child) { - continue; - } - if (child->is_set_as_top_level()) { + if (!child || child->is_set_as_top_level()) { continue; } diff --git a/scene/gui/graph_frame.cpp b/scene/gui/graph_frame.cpp index ca9f7e6fcf..8cd7dbbeb5 100644 --- a/scene/gui/graph_frame.cpp +++ b/scene/gui/graph_frame.cpp @@ -160,14 +160,10 @@ void GraphFrame::_resort() { Point2 offset = Point2(sb_panel->get_margin(SIDE_LEFT), sb_panel->get_margin(SIDE_TOP) + titlebar_min_size.height + sb_titlebar->get_minimum_size().height); for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || !child->is_visible_in_tree()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } - if (child->is_set_as_top_level()) { - continue; - } - fit_child_in_rect(child, Rect2(offset, size)); } } @@ -325,8 +321,8 @@ Size2 GraphFrame::get_minimum_size() const { Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size(); for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || !child->is_visible() || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index d035515b51..d804f83e1c 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -175,9 +175,8 @@ void GraphNode::_resort() { HashMap<Control *, _MinSizeCache> min_size_cache; for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - - if (!child || !child->is_visible_in_tree() || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } @@ -218,8 +217,8 @@ void GraphNode::_resort() { bool refit_successful = true; for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || !child->is_visible_in_tree() || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } @@ -256,8 +255,8 @@ void GraphNode::_resort() { int width = new_size.width - sb_panel->get_minimum_size().width; int valid_children_idx = 0; for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || !child->is_visible_in_tree() || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } @@ -605,6 +604,14 @@ void GraphNode::set_slot_draw_stylebox(int p_slot_index, bool p_enable) { emit_signal(SNAME("slot_updated"), p_slot_index); } +void GraphNode::set_ignore_invalid_connection_type(bool p_ignore) { + ignore_invalid_connection_type = p_ignore; +} + +bool GraphNode::is_ignoring_valid_connection_type() const { + return ignore_invalid_connection_type; +} + Size2 GraphNode::get_minimum_size() const { Ref<StyleBox> sb_panel = theme_cache.panel; Ref<StyleBox> sb_titlebar = theme_cache.titlebar; @@ -614,8 +621,8 @@ Size2 GraphNode::get_minimum_size() const { Size2 minsize = titlebar_hbox->get_minimum_size() + sb_titlebar->get_minimum_size(); for (int i = 0; i < get_child_count(false); i++) { - Control *child = Object::cast_to<Control>(get_child(i, false)); - if (!child || !child->is_visible() || child->is_set_as_top_level()) { + Control *child = as_sortable_control(get_child(i, false)); + if (!child) { continue; } @@ -860,6 +867,9 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "slot_index"), &GraphNode::is_slot_draw_stylebox); ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "slot_index", "enable"), &GraphNode::set_slot_draw_stylebox); + ClassDB::bind_method(D_METHOD("set_ignore_invalid_connection_type", "ignore"), &GraphNode::set_ignore_invalid_connection_type); + ClassDB::bind_method(D_METHOD("is_ignoring_valid_connection_type"), &GraphNode::is_ignoring_valid_connection_type); + ClassDB::bind_method(D_METHOD("get_input_port_count"), &GraphNode::get_input_port_count); ClassDB::bind_method(D_METHOD("get_input_port_position", "port_idx"), &GraphNode::get_input_port_position); ClassDB::bind_method(D_METHOD("get_input_port_type", "port_idx"), &GraphNode::get_input_port_type); @@ -875,6 +885,8 @@ void GraphNode::_bind_methods() { GDVIRTUAL_BIND(_draw_port, "slot_index", "position", "left", "color") ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_invalid_connection_type"), "set_ignore_invalid_connection_type", "is_ignoring_valid_connection_type"); + ADD_SIGNAL(MethodInfo("slot_updated", PropertyInfo(Variant::INT, "slot_index"))); BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphNode, panel); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 71cc322baa..27af3192c8 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -95,6 +95,8 @@ class GraphNode : public GraphElement { bool port_pos_dirty = true; + bool ignore_invalid_connection_type = false; + void _port_pos_update(); protected: @@ -147,6 +149,9 @@ public: bool is_slot_draw_stylebox(int p_slot_index) const; void set_slot_draw_stylebox(int p_slot_index, bool p_enable); + void set_ignore_invalid_connection_type(bool p_ignore); + bool is_ignoring_valid_connection_type() const; + int get_input_port_count(); Vector2 get_input_port_position(int p_port_idx); int get_input_port_type(int p_port_idx); diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index a4baf3bb8d..a67bba786b 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -44,11 +44,8 @@ void GridContainer::_notification(int p_what) { // Compute the per-column/per-row data. int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } @@ -186,8 +183,8 @@ void GridContainer::_notification(int p_what) { valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } int row = valid_controls_index / columns; @@ -282,8 +279,8 @@ Size2 GridContainer::get_minimum_size() const { int valid_controls_index = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } int row = valid_controls_index / columns; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 8376ef48b6..1dd13f2ebf 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1882,6 +1882,7 @@ void ItemList::_bind_methods() { Item defaults(true); base_property_helper.set_prefix("item_"); + base_property_helper.set_array_length_getter(&ItemList::get_item_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, &ItemList::set_item_text, &ItemList::get_item_text); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &ItemList::set_item_icon, &ItemList::get_item_icon); base_property_helper.register_property(PropertyInfo(Variant::BOOL, "selectable"), defaults.selectable, &ItemList::set_item_selectable, &ItemList::is_item_selectable); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index ddfe202c13..c263e14b8a 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1805,7 +1805,6 @@ void LineEdit::clear_internal() { } Size2 LineEdit::get_minimum_size() const { - Ref<StyleBox> style = theme_cache.normal; Ref<Font> font = theme_cache.font; int font_size = theme_cache.font_size; @@ -1834,7 +1833,8 @@ Size2 LineEdit::get_minimum_size() const { } min_size.width += icon_max_width; - return style->get_minimum_size() + min_size; + Size2 style_min_size = theme_cache.normal->get_minimum_size().max(theme_cache.read_only->get_minimum_size()); + return style_min_size + min_size; } void LineEdit::deselect() { diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index 6d331afbb5..06e4a7cc13 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -37,13 +37,7 @@ Size2 MarginContainer::get_minimum_size() const { 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()) { + if (!c || !c->is_visible() || c->is_set_as_top_level()) { continue; } @@ -103,13 +97,10 @@ void MarginContainer::_notification(int p_what) { Size2 s = get_size(); for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } int w = s.width - theme_cache.margin_left - theme_cache.margin_right; int h = s.height - theme_cache.margin_top - theme_cache.margin_bottom; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index e83d9c7c1b..998f99b2f9 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -190,6 +190,7 @@ void MenuButton::_bind_methods() { PopupMenu::Item defaults(true); base_property_helper.set_prefix("popup/item_"); + base_property_helper.set_array_length_getter(&MenuButton::get_item_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon); base_property_helper.register_property(PropertyInfo(Variant::INT, "checkable", PROPERTY_HINT_ENUM, "No,As Checkbox,As Radio Button"), defaults.checkable_type); diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index e2ae824e60..7ebf8a63c2 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -111,7 +111,7 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) { queue_redraw(); update_minimum_size(); - emit_signal(SceneStringNames::get_singleton()->texture_changed); + emit_signal(SceneStringName(texture_changed)); } Ref<Texture2D> NinePatchRect::get_texture() const { diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 509c6aca99..0f161a014a 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -571,6 +571,7 @@ void OptionButton::_bind_methods() { PopupMenu::Item defaults(true); base_property_helper.set_prefix("popup/item_"); + base_property_helper.set_array_length_getter(&OptionButton::get_item_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, &OptionButton::_dummy_setter, &OptionButton::get_item_text); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &OptionButton::_dummy_setter, &OptionButton::get_item_icon); base_property_helper.register_property(PropertyInfo(Variant::INT, "id", PROPERTY_HINT_RANGE, "0,10,1,or_greater"), defaults.id, &OptionButton::_dummy_setter, &OptionButton::get_item_id); diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index ef85919859..76fde26b26 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -35,11 +35,8 @@ Size2 PanelContainer::get_minimum_size() const { Size2 ms; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } @@ -87,11 +84,8 @@ void PanelContainer::_notification(int p_what) { } for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 87383283fd..97b33c4f74 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -77,6 +77,9 @@ void Popup::_notification(int p_what) { _initialize_visible_parents(); } else { _deinitialize_visible_parents(); + if (hide_reason == HIDE_REASON_NONE) { + hide_reason = HIDE_REASON_CANCELED; + } emit_signal(SNAME("popup_hide")); popped_up = false; } @@ -87,6 +90,7 @@ void Popup::_notification(int p_what) { if (!is_in_edited_scene_root()) { if (has_focus()) { popped_up = true; + hide_reason = HIDE_REASON_NONE; } } } break; @@ -100,6 +104,7 @@ void Popup::_notification(int p_what) { case NOTIFICATION_WM_CLOSE_REQUEST: { if (!is_in_edited_scene_root()) { + hide_reason = HIDE_REASON_UNFOCUSED; _close_pressed(); } } break; @@ -114,6 +119,7 @@ void Popup::_notification(int p_what) { void Popup::_parent_focused() { if (popped_up && get_flag(FLAG_POPUP)) { + hide_reason = HIDE_REASON_UNFOCUSED; _close_pressed(); } } diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 48818686f7..69a81ad98c 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -43,6 +43,16 @@ class Popup : public Window { LocalVector<Window *> visible_parents; bool popped_up = false; +public: + enum HideReason { + HIDE_REASON_NONE, + HIDE_REASON_CANCELED, // E.g., because of rupture of UI flow (app unfocused). Includes closed programmatically. + HIDE_REASON_UNFOCUSED, // E.g., user clicked outside. + }; + +private: + HideReason hide_reason = HIDE_REASON_NONE; + void _initialize_visible_parents(); void _deinitialize_visible_parents(); @@ -60,6 +70,8 @@ protected: virtual void _post_popup() override; public: + HideReason get_hide_reason() const { return hide_reason; } + Popup(); ~Popup(); }; diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 9b991972be..5865fe141b 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -2808,6 +2808,7 @@ void PopupMenu::_bind_methods() { Item defaults(true); base_property_helper.set_prefix("item_"); + base_property_helper.set_array_length_getter(&PopupMenu::get_item_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "text"), defaults.text, &PopupMenu::set_item_text, &PopupMenu::get_item_text); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &PopupMenu::set_item_icon, &PopupMenu::get_item_icon); base_property_helper.register_property(PropertyInfo(Variant::INT, "checkable", PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"), defaults.checkable_type, &PopupMenu::_set_item_checkable_type, &PopupMenu::_get_item_checkable_type); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index b2617e6fc7..90ce01e383 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -41,7 +41,7 @@ Size2 ProgressBar::get_minimum_size() const { TextLine tl = TextLine(txt, theme_cache.font, theme_cache.font_size); minimum_size.height = MAX(minimum_size.height, theme_cache.background_style->get_minimum_size().height + tl.get_size().y); } else { // this is needed, else the progressbar will collapse - minimum_size = minimum_size.max(Size2(1, 1)); + minimum_size = minimum_size.maxf(1); } return minimum_size; } diff --git a/scene/gui/rich_text_label.compat.inc b/scene/gui/rich_text_label.compat.inc index 626278a405..97739c4b79 100644 --- a/scene/gui/rich_text_label.compat.inc +++ b/scene/gui/rich_text_label.compat.inc @@ -38,9 +38,14 @@ void RichTextLabel::_add_image_bind_compat_80410(const Ref<Texture2D> &p_image, add_image(p_image, p_width, p_height, p_color, p_alignment, p_region, Variant(), false, String(), false); } +bool RichTextLabel::_remove_paragraph_bind_compat_91098(int p_paragraph) { + return remove_paragraph(p_paragraph, false); +} + void RichTextLabel::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("push_meta", "data"), &RichTextLabel::_push_meta_bind_compat_89024); ClassDB::bind_compatibility_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region"), &RichTextLabel::_add_image_bind_compat_80410, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2())); + ClassDB::bind_compatibility_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::_remove_paragraph_bind_compat_91098); } #endif // DISABLE_DEPRECATED diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 0773181239..19b02f33c6 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -3342,7 +3342,7 @@ void RichTextLabel::_remove_frame(HashSet<Item *> &r_erase_list, ItemFrame *p_fr } } -bool RichTextLabel::remove_paragraph(const int p_paragraph) { +bool RichTextLabel::remove_paragraph(int p_paragraph, bool p_no_invalidate) { _stop_thread(); MutexLock data_lock(data_mutex); @@ -3391,8 +3391,44 @@ bool RichTextLabel::remove_paragraph(const int p_paragraph) { selection.click_frame = nullptr; selection.click_item = nullptr; - deselect(); + selection.active = false; + + if (p_no_invalidate) { + // Do not invalidate cache, only update vertical offsets of the paragraphs after deleted one and scrollbar. + int to_line = main->first_invalid_line.load() - 1; + float total_height = (p_paragraph == 0) ? 0 : _calculate_line_vertical_offset(main->lines[p_paragraph - 1]); + for (int i = p_paragraph; i < to_line; i++) { + MutexLock lock(main->lines[to_line - 1].text_buf->get_mutex()); + main->lines[i].offset.y = total_height; + total_height = _calculate_line_vertical_offset(main->lines[i]); + } + updating_scroll = true; + vscroll->set_max(total_height); + updating_scroll = false; + + main->first_invalid_line.store(MAX(main->first_invalid_line.load() - 1, 0)); + main->first_resized_line.store(MAX(main->first_resized_line.load() - 1, 0)); + main->first_invalid_font_line.store(MAX(main->first_invalid_font_line.load() - 1, 0)); + } else { + // Invalidate cache after the deleted paragraph. + main->first_invalid_line.store(MIN(main->first_invalid_line.load(), p_paragraph)); + main->first_resized_line.store(MIN(main->first_resized_line.load(), p_paragraph)); + main->first_invalid_font_line.store(MIN(main->first_invalid_font_line.load(), p_paragraph)); + } + queue_redraw(); + + return true; +} + +bool RichTextLabel::invalidate_paragraph(int p_paragraph) { + _stop_thread(); + MutexLock data_lock(data_mutex); + + if (p_paragraph >= (int)main->lines.size() || p_paragraph < 0) { + return false; + } + // Invalidate cache. main->first_invalid_line.store(MIN(main->first_invalid_line.load(), p_paragraph)); main->first_resized_line.store(MIN(main->first_resized_line.load(), p_paragraph)); main->first_invalid_font_line.store(MIN(main->first_invalid_font_line.load(), p_paragraph)); @@ -5851,7 +5887,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("add_image", "image", "width", "height", "color", "inline_align", "region", "key", "pad", "tooltip", "size_in_percent"), &RichTextLabel::add_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(Variant()), DEFVAL(false), DEFVAL(String()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("update_image", "key", "mask", "image", "width", "height", "color", "inline_align", "region", "pad", "tooltip", "size_in_percent"), &RichTextLabel::update_image, DEFVAL(0), DEFVAL(0), DEFVAL(Color(1.0, 1.0, 1.0)), DEFVAL(INLINE_ALIGNMENT_CENTER), DEFVAL(Rect2()), DEFVAL(false), DEFVAL(String()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("newline"), &RichTextLabel::add_newline); - ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph"), &RichTextLabel::remove_paragraph); + ClassDB::bind_method(D_METHOD("remove_paragraph", "paragraph", "no_invalidate"), &RichTextLabel::remove_paragraph, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("invalidate_paragraph", "paragraph"), &RichTextLabel::invalidate_paragraph); ClassDB::bind_method(D_METHOD("push_font", "font", "font_size"), &RichTextLabel::push_font, DEFVAL(0)); ClassDB::bind_method(D_METHOD("push_font_size", "font_size"), &RichTextLabel::push_font_size); ClassDB::bind_method(D_METHOD("push_normal"), &RichTextLabel::push_normal); diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 371f6724d7..189ee1da6e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -134,6 +134,7 @@ protected: #ifndef DISABLE_DEPRECATED void _push_meta_bind_compat_89024(const Variant &p_meta); void _add_image_bind_compat_80410(const Ref<Texture2D> &p_image, const int p_width, const int p_height, const Color &p_color, InlineAlignment p_alignment, const Rect2 &p_region); + bool _remove_paragraph_bind_compat_91098(int p_paragraph); static void _bind_compatibility_methods(); #endif @@ -664,7 +665,8 @@ public: void add_image(const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), const Variant &p_key = Variant(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false); void update_image(const Variant &p_key, BitField<ImageUpdateMask> p_mask, const Ref<Texture2D> &p_image, int p_width = 0, int p_height = 0, const Color &p_color = Color(1.0, 1.0, 1.0), InlineAlignment p_alignment = INLINE_ALIGNMENT_CENTER, const Rect2 &p_region = Rect2(), bool p_pad = false, const String &p_tooltip = String(), bool p_size_in_percent = false); void add_newline(); - bool remove_paragraph(const int p_paragraph); + bool remove_paragraph(int p_paragraph, bool p_no_invalidate = false); + bool invalidate_paragraph(int p_paragraph); 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)); void _push_def_font(DefaultFont p_def_font); void _push_def_font_var(DefaultFont p_def_font, const Ref<Font> &p_font, int p_size = -1); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 4f49f60d70..6f5d0cdcfb 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -43,11 +43,8 @@ Size2 ScrollContainer::get_minimum_size() const { largest_child_min_size = Size2(); for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } if (c == h_scroll || c == v_scroll) { @@ -308,11 +305,8 @@ void ScrollContainer::_reposition_children() { } for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible()) { - continue; - } - if (c->is_set_as_top_level()) { + Control *c = as_sortable_control(get_child(i)); + if (!c) { continue; } if (c == h_scroll || c == v_scroll) { @@ -542,13 +536,10 @@ PackedStringArray ScrollContainer::get_configuration_warnings() const { int found = 0; for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); + Control *c = as_sortable_control(get_child(i)); if (!c) { continue; } - if (c->is_set_as_top_level()) { - continue; - } if (c == h_scroll || c == v_scroll) { continue; } diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 5f4586a6d5..98b1db4a96 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -127,10 +127,7 @@ Control *SplitContainer::get_containable_child(int p_idx) const { for (int i = 0; i < get_child_count(false); i++) { Control *c = Object::cast_to<Control>(get_child(i, false)); - if (!c || !c->is_visible()) { - continue; - } - if (c->is_set_as_top_level()) { + if (!c || !c->is_visible() || c->is_set_as_top_level()) { continue; } diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 0e130d60af..8a04974479 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -1868,6 +1868,7 @@ void TabBar::_bind_methods() { Tab defaults(true); base_property_helper.set_prefix("tab_"); + base_property_helper.set_array_length_getter(&TabBar::get_tab_count); base_property_helper.register_property(PropertyInfo(Variant::STRING, "title"), defaults.text, &TabBar::set_tab_title, &TabBar::get_tab_title); base_property_helper.register_property(PropertyInfo(Variant::STRING, "tooltip"), defaults.tooltip, &TabBar::set_tab_tooltip, &TabBar::get_tab_tooltip); base_property_helper.register_property(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), defaults.icon, &TabBar::set_tab_icon, &TabBar::get_tab_icon); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index e2feb59a8c..dc53cf82e6 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -147,9 +147,7 @@ void TabContainer::_notification(int p_what) { if (get_tab_count() > 0) { _refresh_tab_names(); } - } break; - case NOTIFICATION_POST_ENTER_TREE: { if (setup_current_tab >= -1) { set_current_tab(setup_current_tab); setup_current_tab = -2; @@ -191,6 +189,25 @@ void TabContainer::_notification(int p_what) { } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible() || setup_current_tab > -2) { + return; + } + + updating_visibility = true; + + // As the visibility change notification will be triggered for all children soon after, + // beat it to the punch and make sure that the correct node is the only one visible first. + // Otherwise, it can prevent a tab change done right before this container was made visible. + Vector<Control *> controls = _get_tab_controls(); + int current = get_current_tab(); + for (int i = 0; i < controls.size(); i++) { + controls[i]->set_visible(i == current); + } + + updating_visibility = false; + } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { @@ -611,6 +628,7 @@ void TabContainer::set_current_tab(int p_current) { setup_current_tab = p_current; return; } + tab_bar->set_current_tab(p_current); } diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 4fda49a877..49cfa8a030 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -4494,6 +4494,9 @@ void TextEdit::set_multiple_carets_enabled(bool p_enabled) { multi_carets_enabled = p_enabled; if (!multi_carets_enabled) { remove_secondary_carets(); + multicaret_edit_count = 0; + multicaret_edit_ignore_carets.clear(); + multicaret_edit_merge_queued = false; } } @@ -4568,6 +4571,9 @@ int TextEdit::get_caret_count() const { } void TextEdit::add_caret_at_carets(bool p_below) { + if (!multi_carets_enabled) { + return; + } const int last_line_max_wrap = get_line_wrap_count(text.size() - 1); begin_multicaret_edit(); @@ -4594,6 +4600,7 @@ void TextEdit::add_caret_at_carets(bool p_below) { // Add a new caret. int new_caret_index = add_caret(caret_line, caret_column); + ERR_FAIL_COND_MSG(new_caret_index < 0, "Failed to add a caret."); // Copy the selection origin and last fit. set_selection_origin_line(selection_origin_line, true, -1, new_caret_index); @@ -4733,7 +4740,7 @@ void TextEdit::collapse_carets(int p_from_line, int p_from_column, int p_to_line // Caret was in the collapsed area. set_caret_line(collapse_line, false, true, -1, i); set_caret_column(collapse_column, false, i); - if (is_in_mulitcaret_edit()) { + if (is_in_mulitcaret_edit() && get_caret_count() > 1) { multicaret_edit_ignore_carets.insert(i); } any_collapsed = true; @@ -4746,7 +4753,7 @@ void TextEdit::collapse_carets(int p_from_line, int p_from_column, int p_to_line deselect(i); set_caret_line(collapse_line, false, true, -1, i); set_caret_column(collapse_column, false, i); - if (is_in_mulitcaret_edit()) { + if (is_in_mulitcaret_edit() && get_caret_count() > 1) { multicaret_edit_ignore_carets.insert(i); } any_collapsed = true; @@ -4872,10 +4879,16 @@ void TextEdit::merge_overlapping_carets() { // Starts a multicaret edit operation. Call this before iterating over the carets and call [end_multicaret_edit] afterwards. void TextEdit::begin_multicaret_edit() { + if (!multi_carets_enabled) { + return; + } multicaret_edit_count++; } void TextEdit::end_multicaret_edit() { + if (!multi_carets_enabled) { + return; + } if (multicaret_edit_count > 0) { multicaret_edit_count--; } @@ -7060,7 +7073,7 @@ void TextEdit::_paste_internal(int p_caret) { begin_complex_operation(); begin_multicaret_edit(); Vector<int> sorted_carets = get_sorted_carets(); - for (int i = 0; i < get_caret_count(); i++) { + for (int i = 0; i < sorted_carets.size(); i++) { int caret_index = sorted_carets[i]; if (p_caret != -1 && p_caret != caret_index) { continue; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 0b197c8c02..df90257e03 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -103,7 +103,7 @@ bool TextureButton::has_point(const Point2 &p_point) const { point *= scale; // finally, we need to check if the point is inside a rectangle with a position >= 0,0 and a size <= mask_size - rect.position = Point2().max(_texture_region.position); + rect.position = _texture_region.position.maxf(0); rect.size = mask_size.min(_texture_region.size); } diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index fc9ac2ab18..dc48945d9b 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -249,7 +249,7 @@ Point2 TextureProgressBar::get_relative_center() { p += rad_center_off; p.x /= progress->get_width(); p.y /= progress->get_height(); - p = p.clamp(Point2(), Point2(1, 1)); + p = p.clampf(0, 1); return p; } @@ -526,7 +526,7 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> points; for (const float &f : pts) { Point2 uv = unit_val_to_uv(f); - if (uvs.find(uv) >= 0) { + if (uvs.has(uv)) { continue; } points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y)); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b17d345f1f..e2de585a69 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -2828,7 +2828,7 @@ 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_item->first_child && (p_pos.x >= x_ofs && p_pos.x < (x_ofs + theme_cache.item_margin))) { + if (!p_item->disable_folding && !hide_folding && p_item->first_child && (p_pos.x < (x_ofs + theme_cache.item_margin))) { if (enable_recursive_folding && p_mod->is_shift_pressed()) { p_item->set_collapsed_recursive(!p_item->is_collapsed()); } else { @@ -3152,9 +3152,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int } void Tree::_text_editor_popup_modal_close() { - if (Input::get_singleton()->is_key_pressed(Key::ESCAPE) || - Input::get_singleton()->is_key_pressed(Key::KP_ENTER) || - Input::get_singleton()->is_key_pressed(Key::ENTER)) { + if (popup_editor->get_hide_reason() == Popup::HIDE_REASON_CANCELED) { return; } @@ -3429,7 +3427,7 @@ Rect2 Tree::_get_content_rect() const { const real_t v_size = v_scroll->is_visible() ? (v_scroll->get_combined_minimum_size().x + theme_cache.scrollbar_h_separation) : 0; const real_t h_size = h_scroll->is_visible() ? (h_scroll->get_combined_minimum_size().y + theme_cache.scrollbar_v_separation) : 0; const Point2 scroll_begin = _get_scrollbar_layout_rect().get_end() - Vector2(v_size, h_size); - const Size2 offset = (content_rect.get_end() - scroll_begin).max(Vector2(0, 0)); + const Size2 offset = (content_rect.get_end() - scroll_begin).maxf(0); return content_rect.grow_individual(0, 0, -offset.x, -offset.y); } diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 687a9e46a0..e581d0b5db 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -163,7 +163,7 @@ void VideoStreamPlayer::_notification(int p_notification) { play(); return; } - emit_signal(SceneStringNames::get_singleton()->finished); + emit_signal(SceneStringName(finished)); } } break; @@ -460,7 +460,7 @@ StringName VideoStreamPlayer::get_bus() const { return bus; } } - return SceneStringNames::get_singleton()->Master; + return SceneStringName(Master); } void VideoStreamPlayer::_validate_property(PropertyInfo &p_property) const { |
