diff options
Diffstat (limited to 'scene/gui')
66 files changed, 2638 insertions, 1726 deletions
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp index 181d1bf33b..b59eda465e 100644 --- a/scene/gui/aspect_ratio_container.cpp +++ b/scene/gui/aspect_ratio_container.cpp @@ -70,6 +70,24 @@ void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vert queue_sort(); } +Vector<int> AspectRatioContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> AspectRatioContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void AspectRatioContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { diff --git a/scene/gui/aspect_ratio_container.h b/scene/gui/aspect_ratio_container.h index 4a168bad14..6740e2f329 100644 --- a/scene/gui/aspect_ratio_container.h +++ b/scene/gui/aspect_ratio_container.h @@ -72,6 +72,9 @@ public: void set_alignment_vertical(AlignmentMode p_alignment_vertical); AlignmentMode get_alignment_vertical() const { return alignment_vertical; } + + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; }; VARIANT_ENUM_CAST(AspectRatioContainer::StretchMode); diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index da2ef6c5ec..0338326bbe 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -81,42 +81,50 @@ void BaseButton::gui_input(const Ref<InputEvent> &p_event) { } void BaseButton::_notification(int p_what) { - if (p_what == NOTIFICATION_MOUSE_ENTER) { - status.hovering = true; - update(); - } + switch (p_what) { + case NOTIFICATION_MOUSE_ENTER: { + status.hovering = true; + update(); + } break; - if (p_what == NOTIFICATION_MOUSE_EXIT) { - status.hovering = false; - update(); - } - if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) { - if (status.press_attempt) { - status.press_attempt = false; + case NOTIFICATION_MOUSE_EXIT: { + status.hovering = false; update(); - } - } + } break; - if (p_what == NOTIFICATION_FOCUS_ENTER) { - update(); - } + case NOTIFICATION_DRAG_BEGIN: + case NOTIFICATION_SCROLL_BEGIN: { + if (status.press_attempt) { + status.press_attempt = false; + update(); + } + } break; - if (p_what == NOTIFICATION_FOCUS_EXIT) { - if (status.press_attempt) { - status.press_attempt = false; - update(); - } else if (status.hovering) { + case NOTIFICATION_FOCUS_ENTER: { update(); - } - } + } break; - if (p_what == NOTIFICATION_EXIT_TREE || (p_what == NOTIFICATION_VISIBILITY_CHANGED && !is_visible_in_tree())) { - if (!toggle_mode) { - status.pressed = false; - } - status.hovering = false; - status.press_attempt = false; - status.pressing_inside = false; + case NOTIFICATION_FOCUS_EXIT: { + if (status.press_attempt) { + status.press_attempt = false; + update(); + } else if (status.hovering) { + update(); + } + } break; + + case NOTIFICATION_VISIBILITY_CHANGED: + case NOTIFICATION_EXIT_TREE: { + if (p_what == NOTIFICATION_VISIBILITY_CHANGED && is_visible_in_tree()) { + break; + } + if (!toggle_mode) { + status.pressed = false; + } + status.hovering = false; + status.press_attempt = false; + status.pressing_inside = false; + } break; } } diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 0bcad4fc0e..6bfffe7575 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -31,6 +31,7 @@ #ifndef BASE_BUTTON_H #define BASE_BUTTON_H +#include "core/input/shortcut.h" #include "scene/gui/control.h" class ButtonGroup; diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 9827bd0cef..251648da69 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "box_container.h" + #include "label.h" #include "margin_container.h" @@ -294,9 +295,11 @@ void BoxContainer::_notification(int p_what) { case NOTIFICATION_SORT_CHILDREN: { _resort(); } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { queue_sort(); @@ -331,6 +334,30 @@ Control *BoxContainer::add_spacer(bool p_begin) { return c; } +Vector<int> BoxContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (!vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> BoxContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + BoxContainer::BoxContainer(bool p_vertical) { vertical = p_vertical; } diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index 68d55e1aaf..3043c3ea45 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -62,6 +62,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + BoxContainer(bool p_vertical = false); }; diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 3ed1b873af..27e8b102be 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -78,6 +78,7 @@ void Button::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { update(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: { xl_text = atr(text); _shape(); @@ -85,12 +86,14 @@ void Button::_notification(int p_what) { update_minimum_size(); update(); } break; + case NOTIFICATION_THEME_CHANGED: { _shape(); update_minimum_size(); update(); } break; + case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2 size = get_size(); diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index f3306783f3..33ec4006ae 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -69,22 +69,32 @@ bool CenterContainer::is_using_top_left() const { return use_top_left; } +Vector<int> CenterContainer::get_allowed_size_flags_horizontal() const { + return Vector<int>(); +} + +Vector<int> CenterContainer::get_allowed_size_flags_vertical() const { + return Vector<int>(); +} + void CenterContainer::_notification(int p_what) { - if (p_what == NOTIFICATION_SORT_CHILDREN) { - Size2 size = get_size(); - for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c) { - continue; - } - if (c->is_set_as_top_level()) { - continue; - } + switch (p_what) { + case NOTIFICATION_SORT_CHILDREN: { + Size2 size = get_size(); + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c) { + continue; + } + if (c->is_set_as_top_level()) { + continue; + } - Size2 minsize = c->get_combined_minimum_size(); - Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor(); - fit_child_in_rect(c, Rect2(ofs, minsize)); - } + Size2 minsize = c->get_combined_minimum_size(); + Point2 ofs = use_top_left ? (-minsize * 0.5).floor() : ((size - minsize) / 2.0).floor(); + fit_child_in_rect(c, Rect2(ofs, minsize)); + } + } break; } } diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h index 16a10c8070..c35e0c4e29 100644 --- a/scene/gui/center_container.h +++ b/scene/gui/center_container.h @@ -48,6 +48,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + CenterContainer(); }; diff --git a/scene/gui/check_box.cpp b/scene/gui/check_box.cpp index da2d4369d1..063a154bb2 100644 --- a/scene/gui/check_box.cpp +++ b/scene/gui/check_box.cpp @@ -84,34 +84,40 @@ Size2 CheckBox::get_minimum_size() const { } void CheckBox::_notification(int p_what) { - if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || (p_what == NOTIFICATION_TRANSLATION_CHANGED))) { - if (is_layout_rtl()) { - _set_internal_margin(SIDE_LEFT, 0.f); - _set_internal_margin(SIDE_RIGHT, get_icon_size().width); - } else { - _set_internal_margin(SIDE_LEFT, get_icon_size().width); - _set_internal_margin(SIDE_RIGHT, 0.f); - } - } else if (p_what == NOTIFICATION_DRAW) { - RID ci = get_canvas_item(); - - Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : "")); - Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : "")); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - - Vector2 ofs; - if (is_layout_rtl()) { - ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width; - } else { - ofs.x = sb->get_margin(SIDE_LEFT); - } - ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust")); - - if (is_pressed()) { - on->draw(ci, ofs); - } else { - off->draw(ci, ofs); - } + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, 0.f); + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } else { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + _set_internal_margin(SIDE_RIGHT, 0.f); + } + } break; + + case NOTIFICATION_DRAW: { + RID ci = get_canvas_item(); + + Ref<Texture2D> on = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_checked" : "checked", is_disabled() ? "_disabled" : "")); + Ref<Texture2D> off = Control::get_theme_icon(vformat("%s%s", is_radio() ? "radio_unchecked" : "unchecked", is_disabled() ? "_disabled" : "")); + Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); + + Vector2 ofs; + if (is_layout_rtl()) { + ofs.x = get_size().x - sb->get_margin(SIDE_RIGHT) - get_icon_size().width; + } else { + ofs.x = sb->get_margin(SIDE_LEFT); + } + ofs.y = int((get_size().height - get_icon_size().height) / 2) + get_theme_constant(SNAME("check_vadjust")); + + if (is_pressed()) { + on->draw(ci, ofs); + } else { + off->draw(ci, ofs); + } + } break; } } diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index afb23a540b..5e3131f8a0 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -61,47 +61,53 @@ Size2 CheckButton::get_minimum_size() const { } void CheckButton::_notification(int p_what) { - if ((p_what == NOTIFICATION_THEME_CHANGED) || (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED)) { - if (is_layout_rtl()) { - _set_internal_margin(SIDE_LEFT, get_icon_size().width); - _set_internal_margin(SIDE_RIGHT, 0.f); - } else { - _set_internal_margin(SIDE_LEFT, 0.f); - _set_internal_margin(SIDE_RIGHT, get_icon_size().width); - } - } else if (p_what == NOTIFICATION_DRAW) { - RID ci = get_canvas_item(); - bool rtl = is_layout_rtl(); + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + if (is_layout_rtl()) { + _set_internal_margin(SIDE_LEFT, get_icon_size().width); + _set_internal_margin(SIDE_RIGHT, 0.f); + } else { + _set_internal_margin(SIDE_LEFT, 0.f); + _set_internal_margin(SIDE_RIGHT, get_icon_size().width); + } + } break; - Ref<Texture2D> on; - if (rtl) { - on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored"); - } else { - on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); - } - Ref<Texture2D> off; - if (rtl) { - off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored"); - } else { - off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); - } + case NOTIFICATION_DRAW: { + RID ci = get_canvas_item(); + bool rtl = is_layout_rtl(); - Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); - Vector2 ofs; - Size2 tex_size = get_icon_size(); + Ref<Texture2D> on; + if (rtl) { + on = Control::get_theme_icon(is_disabled() ? "on_disabled_mirrored" : "on_mirrored"); + } else { + on = Control::get_theme_icon(is_disabled() ? "on_disabled" : "on"); + } + Ref<Texture2D> off; + if (rtl) { + off = Control::get_theme_icon(is_disabled() ? "off_disabled_mirrored" : "off_mirrored"); + } else { + off = Control::get_theme_icon(is_disabled() ? "off_disabled" : "off"); + } - if (rtl) { - ofs.x = sb->get_margin(SIDE_LEFT); - } else { - ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); - } - ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust")); + Ref<StyleBox> sb = get_theme_stylebox(SNAME("normal")); + Vector2 ofs; + Size2 tex_size = get_icon_size(); - if (is_pressed()) { - on->draw(ci, ofs); - } else { - off->draw(ci, ofs); - } + if (rtl) { + ofs.x = sb->get_margin(SIDE_LEFT); + } else { + ofs.x = get_size().width - (tex_size.width + sb->get_margin(SIDE_RIGHT)); + } + ofs.y = (get_size().height - tex_size.height) / 2 + get_theme_constant(SNAME("check_vadjust")); + + if (is_pressed()) { + on->draw(ci, ofs); + } else { + off->draw(ci, ofs); + } + } break; } } diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 8cb8a78e8d..3fa0cec302 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -74,6 +74,7 @@ void CodeEdit::_notification(int p_what) { line_length_guideline_color = get_theme_color(SNAME("line_length_guideline_color")); } break; + case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); const Size2 size = get_size(); @@ -84,7 +85,7 @@ void CodeEdit::_notification(int p_what) { if (line_length_guideline_columns.size() > 0) { const int xmargin_beg = style_normal->get_margin(SIDE_LEFT) + get_total_gutter_width(); const int xmargin_end = size.width - style_normal->get_margin(SIDE_RIGHT) - (is_drawing_minimap() ? get_minimap_width() : 0); - const int char_size = Math::round(font->get_char_size('0', 0, font_size).width); + const float char_size = font->get_char_size('0', 0, font_size).width; for (int i = 0; i < line_length_guideline_columns.size(); i++) { const int xoffset = xmargin_beg + char_size * (int)line_length_guideline_columns[i] - get_h_scroll(); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 36ea843d1e..3ea2a9795d 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -33,11 +33,11 @@ #include "core/input/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "scene/main/window.h" #ifdef TOOLS_ENABLED #include "editor/editor_settings.h" #endif -#include "scene/main/window.h" List<Color> ColorPicker::preset_cache; @@ -45,7 +45,6 @@ void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_color(); - #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint()) { if (preset_cache.is_empty()) { @@ -1347,17 +1346,18 @@ void ColorPickerButton::_notification(int p_what) { draw_texture(Control::get_theme_icon(SNAME("overbright_indicator"), SNAME("ColorPicker")), normal->get_offset()); } } break; + case NOTIFICATION_WM_CLOSE_REQUEST: { if (popup) { popup->hide(); } } break; - } - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (popup && !is_visible_in_tree()) { - popup->hide(); - } + case NOTIFICATION_VISIBILITY_CHANGED: { + if (popup && !is_visible_in_tree()) { + popup->hide(); + } + } break; } } diff --git a/scene/gui/color_rect.cpp b/scene/gui/color_rect.cpp index dbac1fc78a..2955f74a0c 100644 --- a/scene/gui/color_rect.cpp +++ b/scene/gui/color_rect.cpp @@ -40,8 +40,10 @@ Color ColorRect::get_color() const { } void ColorRect::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - draw_rect(Rect2(Point2(), get_size()), color); + switch (p_what) { + case NOTIFICATION_DRAW: { + draw_rect(Rect2(Point2(), get_size()), color); + } break; } } diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index 7b213ec314..1dd88371ea 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "container.h" + #include "core/object/message_queue.h" #include "scene/scene_string_names.h" @@ -143,18 +144,46 @@ void Container::queue_sort() { pending_sort = true; } +Vector<int> Container::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + if (GDVIRTUAL_CALL(_get_allowed_size_flags_horizontal, flags)) { + return flags; + } + + flags.append(SIZE_FILL); + flags.append(SIZE_EXPAND); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> Container::get_allowed_size_flags_vertical() const { + Vector<int> flags; + if (GDVIRTUAL_CALL(_get_allowed_size_flags_vertical, flags)) { + return flags; + } + + flags.append(SIZE_FILL); + flags.append(SIZE_EXPAND); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void Container::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { pending_sort = false; queue_sort(); } break; - case NOTIFICATION_RESIZED: { - queue_sort(); - } break; + + case NOTIFICATION_RESIZED: case NOTIFICATION_THEME_CHANGED: { queue_sort(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible_in_tree()) { queue_sort(); @@ -177,6 +206,9 @@ void Container::_bind_methods() { ClassDB::bind_method(D_METHOD("queue_sort"), &Container::queue_sort); ClassDB::bind_method(D_METHOD("fit_child_in_rect", "child", "rect"), &Container::fit_child_in_rect); + GDVIRTUAL_BIND(_get_allowed_size_flags_horizontal); + GDVIRTUAL_BIND(_get_allowed_size_flags_vertical); + BIND_CONSTANT(NOTIFICATION_PRE_SORT_CHILDREN); BIND_CONSTANT(NOTIFICATION_SORT_CHILDREN); diff --git a/scene/gui/container.h b/scene/gui/container.h index 0e986f46ef..9ec4ad3200 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -46,6 +46,9 @@ protected: virtual void move_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_horizontal) + GDVIRTUAL0RC(Vector<int>, _get_allowed_size_flags_vertical) + void _notification(int p_what); static void _bind_methods(); @@ -57,6 +60,9 @@ public: void fit_child_in_rect(Control *p_child, const Rect2 &p_rect); + virtual Vector<int> get_allowed_size_flags_horizontal() const; + virtual Vector<int> get_allowed_size_flags_vertical() const; + TypedArray<String> get_configuration_warnings() const override; Container(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index fdae8e2f1f..46f60c92d9 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -47,7 +47,7 @@ #include "servers/text_server.h" #ifdef TOOLS_ENABLED -#include "editor/plugins/canvas_item_editor_plugin.h" +#include "editor/plugins/control_editor_plugin.h" #endif #ifdef TOOLS_ENABLED @@ -56,51 +56,71 @@ Dictionary Control::_edit_get_state() const { s["rotation"] = get_rotation(); s["scale"] = get_scale(); s["pivot"] = get_pivot_offset(); + Array anchors; anchors.push_back(get_anchor(SIDE_LEFT)); anchors.push_back(get_anchor(SIDE_TOP)); anchors.push_back(get_anchor(SIDE_RIGHT)); anchors.push_back(get_anchor(SIDE_BOTTOM)); s["anchors"] = anchors; + Array offsets; offsets.push_back(get_offset(SIDE_LEFT)); offsets.push_back(get_offset(SIDE_TOP)); offsets.push_back(get_offset(SIDE_RIGHT)); offsets.push_back(get_offset(SIDE_BOTTOM)); s["offsets"] = offsets; + + s["layout_mode"] = _get_layout_mode(); + s["anchors_layout_preset"] = _get_anchors_layout_preset(); + return s; } void Control::_edit_set_state(const Dictionary &p_state) { ERR_FAIL_COND((p_state.size() <= 0) || !p_state.has("rotation") || !p_state.has("scale") || - !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets")); + !p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets") || + !p_state.has("layout_mode") || !p_state.has("anchors_layout_preset")); Dictionary state = p_state; set_rotation(state["rotation"]); set_scale(state["scale"]); set_pivot_offset(state["pivot"]); + Array anchors = state["anchors"]; + + // If anchors are not in their default position, force the anchor layout mode in place of position. + LayoutMode _layout = (LayoutMode)(int)state["layout_mode"]; + if (_layout == LayoutMode::LAYOUT_MODE_POSITION) { + bool anchors_mode = ((real_t)anchors[0] != 0.0 || (real_t)anchors[1] != 0.0 || (real_t)anchors[2] != 0.0 || (real_t)anchors[3] != 0.0); + if (anchors_mode) { + _layout = LayoutMode::LAYOUT_MODE_ANCHORS; + } + } + + _set_layout_mode(_layout); + if (_layout == LayoutMode::LAYOUT_MODE_ANCHORS) { + _set_anchors_layout_preset((int)state["anchors_layout_preset"]); + } + data.anchor[SIDE_LEFT] = anchors[0]; data.anchor[SIDE_TOP] = anchors[1]; data.anchor[SIDE_RIGHT] = anchors[2]; data.anchor[SIDE_BOTTOM] = anchors[3]; + Array offsets = state["offsets"]; data.offset[SIDE_LEFT] = offsets[0]; data.offset[SIDE_TOP] = offsets[1]; data.offset[SIDE_RIGHT] = offsets[2]; data.offset[SIDE_BOTTOM] = offsets[3]; + _size_changed(); } void Control::_edit_set_position(const Point2 &p_position) { -#ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); - set_position(p_position, CanvasItemEditor::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent)); -#else - // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED - set_position(p_position); -#endif + set_position(p_position, ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled() && Object::cast_to<Control>(data.parent)); }; Point2 Control::_edit_get_position() const { @@ -116,15 +136,9 @@ Size2 Control::_edit_get_scale() const { } void Control::_edit_set_rect(const Rect2 &p_edit_rect) { -#ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!Engine::get_singleton()->is_editor_hint(), "This function can only be used from editor plugins."); - set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); - set_size(p_edit_rect.size.snapped(Vector2(1, 1)), CanvasItemEditor::get_singleton()->is_anchors_mode_enabled()); -#else - // Unlikely to happen. TODO: enclose all _edit_ functions into TOOLS_ENABLED - set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1))); - set_size(p_edit_rect.size.snapped(Vector2(1, 1))); -#endif + set_position((get_position() + get_transform().basis_xform(p_edit_rect.position)).snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); + set_size(p_edit_rect.size.snapped(Vector2(1, 1)), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled()); } Rect2 Control::_edit_get_rect() const { @@ -177,6 +191,7 @@ String Control::properties_managed_by_container[] = { "anchor_right", "anchor_bottom", "rect_position", + "rect_rotation", "rect_scale", "rect_size" }; @@ -430,6 +445,7 @@ void Control::_get_property_list(List<PropertyInfo> *p_list) const { } void Control::_validate_property(PropertyInfo &property) const { + // Update theme type variation options. if (property.name == "theme_type_variation") { List<StringName> names; @@ -455,10 +471,99 @@ void Control::_validate_property(PropertyInfo &property) const { property.hint_string = hint_string; } - if (!Object::cast_to<Container>(get_parent())) { - return; + + // Validate which positioning properties should be displayed depending on the parent and the layout mode. + Node *parent_node = get_parent_control(); + if (!parent_node) { + // If there is no parent, display both anchor and container options. + + // Set the layout mode to be disabled with the proper value. + if (property.name == "layout_mode") { + property.hint_string = "Position,Anchors,Container,Uncontrolled"; + property.usage |= PROPERTY_USAGE_READ_ONLY; + } + + // Use the layout mode to display or hide advanced anchoring properties. + bool use_custom_anchors = _get_anchors_layout_preset() == -1; // Custom "preset". + if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) { + property.usage ^= PROPERTY_USAGE_EDITOR; + } + } else if (Object::cast_to<Container>(parent_node)) { + // If the parent is a container, display only container-related properties. + if (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_") || property.name == "anchors_preset" || + (property.name.begins_with("rect_") && property.name != "rect_min_size" && property.name != "rect_clip_content" && property.name != "rect_global_position")) { + property.usage ^= PROPERTY_USAGE_EDITOR; + + } else if (property.name == "layout_mode") { + // Set the layout mode to be disabled with the proper value. + property.hint_string = "Position,Anchors,Container,Uncontrolled"; + property.usage |= PROPERTY_USAGE_READ_ONLY; + } else if (property.name == "size_flags_horizontal" || property.name == "size_flags_vertical") { + // Filter allowed size flags based on the parent container configuration. + Container *parent_container = Object::cast_to<Container>(parent_node); + Vector<int> size_flags; + if (property.name == "size_flags_horizontal") { + size_flags = parent_container->get_allowed_size_flags_horizontal(); + } else if (property.name == "size_flags_vertical") { + size_flags = parent_container->get_allowed_size_flags_vertical(); + } + + // Enforce the order of the options, regardless of what the container provided. + String hint_string; + if (size_flags.has(SIZE_FILL)) { + hint_string += "Fill:1"; + } + if (size_flags.has(SIZE_EXPAND)) { + if (!hint_string.is_empty()) { + hint_string += ","; + } + hint_string += "Expand:2"; + } + if (size_flags.has(SIZE_SHRINK_CENTER)) { + if (!hint_string.is_empty()) { + hint_string += ","; + } + hint_string += "Shrink Center:4"; + } + if (size_flags.has(SIZE_SHRINK_END)) { + if (!hint_string.is_empty()) { + hint_string += ","; + } + hint_string += "Shrink End:8"; + } + + if (hint_string.is_empty()) { + property.hint_string = ""; + property.usage |= PROPERTY_USAGE_READ_ONLY; + } else { + property.hint_string = hint_string; + } + } + } else { + // If the parent is NOT a container or not a control at all, display only anchoring-related properties. + if (property.name.begins_with("size_flags_")) { + property.usage ^= PROPERTY_USAGE_EDITOR; + + } else if (property.name == "layout_mode") { + // Set the layout mode to be enabled with proper options. + property.hint_string = "Position,Anchors"; + } + + // Use the layout mode to display or hide advanced anchoring properties. + bool use_anchors = _get_layout_mode() == LayoutMode::LAYOUT_MODE_ANCHORS; + if (!use_anchors && property.name == "anchors_preset") { + property.usage ^= PROPERTY_USAGE_EDITOR; + } + bool use_custom_anchors = use_anchors && _get_anchors_layout_preset() == -1; // Custom "preset". + if (!use_custom_anchors && (property.name.begins_with("anchor_") || property.name.begins_with("offset_") || property.name.begins_with("grow_"))) { + property.usage ^= PROPERTY_USAGE_EDITOR; + } } + // Disable the property if it's managed by the parent container. + if (!Object::cast_to<Container>(parent_node)) { + return; + } bool property_is_managed_by_container = false; for (unsigned i = 0; i < properties_managed_by_container_count; i++) { property_is_managed_by_container = properties_managed_by_container[i] == property.name; @@ -586,17 +691,17 @@ void Control::_update_canvas_item_transform() { void Control::_notification(int p_notification) { switch (p_notification) { - case NOTIFICATION_ENTER_TREE: { - } break; case NOTIFICATION_POST_ENTER_TREE: { data.minimum_size_valid = false; data.is_rtl_dirty = true; _size_changed(); } break; + case NOTIFICATION_EXIT_TREE: { release_focus(); get_viewport()->_gui_remove_control(this); } break; + case NOTIFICATION_READY: { #ifdef DEBUG_ENABLED connect("ready", callable_mp(this, &Control::_clear_size_warning), varray(), CONNECT_DEFERRED | CONNECT_ONESHOT); @@ -659,6 +764,7 @@ void Control::_notification(int p_notification) { viewport->connect("size_changed", callable_mp(this, &Control::_size_changed)); } } break; + case NOTIFICATION_EXIT_CANVAS: { if (data.parent_canvas_item) { data.parent_canvas_item->disconnect("item_rect_changed", callable_mp(this, &Control::_size_changed)); @@ -679,8 +785,8 @@ void Control::_notification(int p_notification) { data.parent_canvas_item = nullptr; data.parent_window = nullptr; data.is_rtl_dirty = true; - } break; + case NOTIFICATION_MOVED_IN_PARENT: { // some parents need to know the order of the children to draw (like TabContainer) // update if necessary @@ -692,50 +798,52 @@ void Control::_notification(int p_notification) { if (data.RI) { get_viewport()->_gui_set_root_order_dirty(); } - } break; + case NOTIFICATION_RESIZED: { emit_signal(SceneStringNames::get_singleton()->resized); } break; + case NOTIFICATION_DRAW: { _update_canvas_item_transform(); RenderingServer::get_singleton()->canvas_item_set_custom_rect(get_canvas_item(), !data.disable_visibility_clip, Rect2(Point2(), get_size())); RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), data.clip_contents); - //emit_signal(SceneStringNames::get_singleton()->draw); - } break; + case NOTIFICATION_MOUSE_ENTER: { emit_signal(SceneStringNames::get_singleton()->mouse_entered); } break; + case NOTIFICATION_MOUSE_EXIT: { emit_signal(SceneStringNames::get_singleton()->mouse_exited); } break; + case NOTIFICATION_FOCUS_ENTER: { emit_signal(SceneStringNames::get_singleton()->focus_entered); update(); } break; + case NOTIFICATION_FOCUS_EXIT: { emit_signal(SceneStringNames::get_singleton()->focus_exited); update(); } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); update(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible_in_tree()) { if (get_viewport() != nullptr) { get_viewport()->_gui_hide_control(this); } - - //remove key focus - } else { data.minimum_size_valid = false; _size_changed(); } - } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { if (is_inside_tree()) { @@ -1390,6 +1498,54 @@ void Control::_size_changed() { } } +void Control::_set_layout_mode(LayoutMode p_mode) { + bool list_changed = false; + + if (p_mode == LayoutMode::LAYOUT_MODE_POSITION || p_mode == LayoutMode::LAYOUT_MODE_ANCHORS) { + if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)p_mode) { + list_changed = true; + } + + set_meta("_edit_layout_mode", (int)p_mode); + + if (p_mode == LayoutMode::LAYOUT_MODE_POSITION) { + set_meta("_edit_use_custom_anchors", false); + set_anchors_and_offsets_preset(LayoutPreset::PRESET_TOP_LEFT, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); + set_grow_direction_preset(LayoutPreset::PRESET_TOP_LEFT); + } + } else { + if (has_meta("_edit_layout_mode")) { + remove_meta("_edit_layout_mode"); + list_changed = true; + } + } + + if (list_changed) { + notify_property_list_changed(); + } +} + +Control::LayoutMode Control::_get_layout_mode() const { + Node *parent_node = get_parent_control(); + // In these modes the property is read-only. + if (!parent_node) { + return LayoutMode::LAYOUT_MODE_UNCONTROLLED; + } else if (Object::cast_to<Container>(parent_node)) { + return LayoutMode::LAYOUT_MODE_CONTAINER; + } + + // If anchors are not in the top-left position, this is definitely in anchors mode. + if (_get_anchors_layout_preset() != (int)LayoutPreset::PRESET_TOP_LEFT) { + return LayoutMode::LAYOUT_MODE_ANCHORS; + } + // Otherwise check what was saved. + if (has_meta("_edit_layout_mode")) { + return (LayoutMode)(int)get_meta("_edit_layout_mode"); + } + // Or fallback on default. + return LayoutMode::LAYOUT_MODE_POSITION; +} + void Control::set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset, bool p_push_opposite_anchor) { ERR_FAIL_INDEX((int)p_side, 4); @@ -1431,6 +1587,133 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, set_offset(p_side, p_pos); } +void Control::_set_anchors_layout_preset(int p_preset) { + bool list_changed = false; + + if (has_meta("_edit_layout_mode") && (int)get_meta("_edit_layout_mode") != (int)LayoutMode::LAYOUT_MODE_ANCHORS) { + list_changed = true; + set_meta("_edit_layout_mode", (int)LayoutMode::LAYOUT_MODE_ANCHORS); + } + + if (p_preset == -1) { + if (!has_meta("_edit_use_custom_anchors") || !(bool)get_meta("_edit_use_custom_anchors")) { + set_meta("_edit_use_custom_anchors", true); + notify_property_list_changed(); + } + return; // Keep settings as is. + } + + if (!has_meta("_edit_use_custom_anchors") || (bool)get_meta("_edit_use_custom_anchors")) { + list_changed = true; + set_meta("_edit_use_custom_anchors", false); + } + + LayoutPreset preset = (LayoutPreset)p_preset; + // Set correct anchors. + set_anchors_preset(preset); + + // Select correct preset mode. + switch (preset) { + case PRESET_TOP_LEFT: + case PRESET_TOP_RIGHT: + case PRESET_BOTTOM_LEFT: + case PRESET_BOTTOM_RIGHT: + case PRESET_CENTER_LEFT: + case PRESET_CENTER_TOP: + case PRESET_CENTER_RIGHT: + case PRESET_CENTER_BOTTOM: + case PRESET_CENTER: + set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_KEEP_SIZE); + break; + case PRESET_LEFT_WIDE: + case PRESET_TOP_WIDE: + case PRESET_RIGHT_WIDE: + case PRESET_BOTTOM_WIDE: + case PRESET_VCENTER_WIDE: + case PRESET_HCENTER_WIDE: + case PRESET_WIDE: + set_offsets_preset(preset, LayoutPresetMode::PRESET_MODE_MINSIZE); + break; + } + + // Select correct grow directions. + set_grow_direction_preset(preset); + + if (list_changed) { + notify_property_list_changed(); + } +} + +int Control::_get_anchors_layout_preset() const { + // If the custom preset was selected by user, use it. + if (has_meta("_edit_use_custom_anchors") && (bool)get_meta("_edit_use_custom_anchors")) { + return -1; + } + + // Check anchors to determine if the current state matches a preset, or not. + + float left = get_anchor(SIDE_LEFT); + float right = get_anchor(SIDE_RIGHT); + float top = get_anchor(SIDE_TOP); + float bottom = get_anchor(SIDE_BOTTOM); + + if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) { + return (int)LayoutPreset::PRESET_TOP_LEFT; + } + if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) { + return (int)LayoutPreset::PRESET_TOP_RIGHT; + } + if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_END && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_BOTTOM_LEFT; + } + if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_BOTTOM_RIGHT; + } + + if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == 0.5 && bottom == 0.5) { + return (int)LayoutPreset::PRESET_CENTER_LEFT; + } + if (left == ANCHOR_END && right == ANCHOR_END && top == 0.5 && bottom == 0.5) { + return (int)LayoutPreset::PRESET_CENTER_RIGHT; + } + if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) { + return (int)LayoutPreset::PRESET_CENTER_TOP; + } + if (left == 0.5 && right == 0.5 && top == ANCHOR_END && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_CENTER_BOTTOM; + } + if (left == 0.5 && right == 0.5 && top == 0.5 && bottom == 0.5) { + return (int)LayoutPreset::PRESET_CENTER; + } + + if (left == ANCHOR_BEGIN && right == ANCHOR_BEGIN && top == ANCHOR_BEGIN && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_LEFT_WIDE; + } + if (left == ANCHOR_END && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_RIGHT_WIDE; + } + if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_BEGIN) { + return (int)LayoutPreset::PRESET_TOP_WIDE; + } + if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_END && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_BOTTOM_WIDE; + } + + if (left == 0.5 && right == 0.5 && top == ANCHOR_BEGIN && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_VCENTER_WIDE; + } + if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == 0.5 && bottom == 0.5) { + return (int)LayoutPreset::PRESET_HCENTER_WIDE; + } + + if (left == ANCHOR_BEGIN && right == ANCHOR_END && top == ANCHOR_BEGIN && bottom == ANCHOR_END) { + return (int)LayoutPreset::PRESET_WIDE; + } + + // Does not match any preset, return "Custom". + return -1; +} + void Control::set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets) { ERR_FAIL_INDEX((int)p_preset, 16); @@ -1687,6 +1970,62 @@ void Control::set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPreset set_offsets_preset(p_preset, p_resize_mode, p_margin); } +void Control::set_grow_direction_preset(LayoutPreset p_preset) { + // Select correct horizontal grow direction. + switch (p_preset) { + case PRESET_TOP_LEFT: + case PRESET_BOTTOM_LEFT: + case PRESET_CENTER_LEFT: + case PRESET_LEFT_WIDE: + set_h_grow_direction(GrowDirection::GROW_DIRECTION_END); + break; + case PRESET_TOP_RIGHT: + case PRESET_BOTTOM_RIGHT: + case PRESET_CENTER_RIGHT: + case PRESET_RIGHT_WIDE: + set_h_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN); + break; + case PRESET_CENTER_TOP: + case PRESET_CENTER_BOTTOM: + case PRESET_CENTER: + case PRESET_TOP_WIDE: + case PRESET_BOTTOM_WIDE: + case PRESET_VCENTER_WIDE: + case PRESET_HCENTER_WIDE: + case PRESET_WIDE: + set_h_grow_direction(GrowDirection::GROW_DIRECTION_BOTH); + break; + } + + // Select correct vertical grow direction. + switch (p_preset) { + case PRESET_TOP_LEFT: + case PRESET_TOP_RIGHT: + case PRESET_CENTER_TOP: + case PRESET_TOP_WIDE: + set_v_grow_direction(GrowDirection::GROW_DIRECTION_END); + break; + + case PRESET_BOTTOM_LEFT: + case PRESET_BOTTOM_RIGHT: + case PRESET_CENTER_BOTTOM: + case PRESET_BOTTOM_WIDE: + set_v_grow_direction(GrowDirection::GROW_DIRECTION_BEGIN); + break; + + case PRESET_CENTER_LEFT: + case PRESET_CENTER_RIGHT: + case PRESET_CENTER: + case PRESET_LEFT_WIDE: + case PRESET_RIGHT_WIDE: + case PRESET_VCENTER_WIDE: + case PRESET_HCENTER_WIDE: + case PRESET_WIDE: + set_v_grow_direction(GrowDirection::GROW_DIRECTION_BOTH); + break; + } +} + real_t Control::get_anchor(Side p_side) const { ERR_FAIL_INDEX_V(int(p_side), 4, 0.0); @@ -2847,14 +3186,22 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("accept_event"), &Control::accept_event); ClassDB::bind_method(D_METHOD("get_minimum_size"), &Control::get_minimum_size); ClassDB::bind_method(D_METHOD("get_combined_minimum_size"), &Control::get_combined_minimum_size); + + ClassDB::bind_method(D_METHOD("_set_layout_mode", "mode"), &Control::_set_layout_mode); + ClassDB::bind_method(D_METHOD("_get_layout_mode"), &Control::_get_layout_mode); + ClassDB::bind_method(D_METHOD("_set_anchors_layout_preset", "preset"), &Control::_set_anchors_layout_preset); + ClassDB::bind_method(D_METHOD("_get_anchors_layout_preset"), &Control::_get_anchors_layout_preset); ClassDB::bind_method(D_METHOD("set_anchors_preset", "preset", "keep_offsets"), &Control::set_anchors_preset, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("set_anchors_and_offsets_preset", "preset", "resize_mode", "margin"), &Control::set_anchors_and_offsets_preset, DEFVAL(PRESET_MODE_MINSIZE), DEFVAL(0)); + ClassDB::bind_method(D_METHOD("_set_anchor", "side", "anchor"), &Control::_set_anchor); ClassDB::bind_method(D_METHOD("set_anchor", "side", "anchor", "keep_offset", "push_opposite_anchor"), &Control::set_anchor, DEFVAL(false), DEFVAL(true)); ClassDB::bind_method(D_METHOD("get_anchor", "side"), &Control::get_anchor); ClassDB::bind_method(D_METHOD("set_offset", "side", "offset"), &Control::set_offset); + ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset); ClassDB::bind_method(D_METHOD("set_anchor_and_offset", "side", "anchor", "offset", "push_opposite_anchor"), &Control::set_anchor_and_offset, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("set_begin", "position"), &Control::set_begin); ClassDB::bind_method(D_METHOD("set_end", "position"), &Control::set_end); ClassDB::bind_method(D_METHOD("set_position", "position", "keep_offsets"), &Control::set_position, DEFVAL(false)); @@ -2868,7 +3215,6 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_rotation", "radians"), &Control::set_rotation); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); - ClassDB::bind_method(D_METHOD("get_offset", "offset"), &Control::get_offset); ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin); ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end); ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); @@ -2996,37 +3342,54 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate); ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating); - ADD_GROUP("Anchor", "anchor_"); + ADD_GROUP("Layout", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_mode", PROPERTY_HINT_ENUM, "Position,Anchors,Container,Uncontrolled", PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_layout_mode", "_get_layout_mode"); + ADD_PROPERTY_DEFAULT("layout_mode", LayoutMode::LAYOUT_MODE_POSITION); + + const String anchors_presets_options = "Custom:-1,PresetWide:15," + "PresetTopLeft:0,PresetTopRight:1,PresetBottomRight:3,PresetBottomLeft:2," + "PresetCenterLeft:4,PresetCenterTop:5,PresetCenterRight:6,PresetCenterBottom:7,PresetCenter:8," + "PresetLeftWide:9,PresetTopWide:10,PresetRightWide:11,PresetBottomWide:12,PresetVCenterWide:13,PresetHCenterWide:14"; + + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchors_preset", PROPERTY_HINT_ENUM, anchors_presets_options, PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_anchors_layout_preset", "_get_anchors_layout_preset"); + ADD_PROPERTY_DEFAULT("anchors_preset", -1); + + ADD_SUBGROUP_INDENT("Anchor Points", "anchor_", 1); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_left", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_LEFT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_top", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_TOP); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_right", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anchor_bottom", PROPERTY_HINT_RANGE, "0,1,0.001,or_lesser,or_greater"), "_set_anchor", "get_anchor", SIDE_BOTTOM); - ADD_GROUP("Offset", "offset_"); + ADD_SUBGROUP_INDENT("Anchor Offsets", "offset_", 1); ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_left", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_LEFT); ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_top", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_TOP); ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_right", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_RIGHT); ADD_PROPERTYI(PropertyInfo(Variant::INT, "offset_bottom", PROPERTY_HINT_RANGE, "-4096,4096"), "set_offset", "get_offset", SIDE_BOTTOM); - ADD_GROUP("Grow Direction", "grow_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_h_grow_direction", "get_h_grow_direction"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction"); - - ADD_GROUP("Layout Direction", "layout_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction"); - - ADD_GROUP("Auto Translate", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); + ADD_SUBGROUP_INDENT("Grow Direction", "grow_", 1); + ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_horizontal", PROPERTY_HINT_ENUM, "Left,Right,Both"), "set_h_grow_direction", "get_h_grow_direction"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Top,Bottom,Both"), "set_v_grow_direction", "get_v_grow_direction"); - ADD_GROUP("Rect", "rect_"); + ADD_SUBGROUP("Rectangle", "rect_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_global_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "_set_global_position", "get_global_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_size", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_size", "get_size"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_min_size"), "set_custom_minimum_size", "get_custom_minimum_size"); + + ADD_SUBGROUP("Transform", "rect_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rect_rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_pivot_offset"), "set_pivot_offset", "get_pivot_offset"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "rect_clip_content"), "set_clip_contents", "is_clipping_contents"); + + ADD_SUBGROUP("Container Sizing", "size_flags_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_v_size_flags", "get_v_size_flags"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); + + ADD_GROUP("Auto Translate", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); ADD_GROUP("Hint", "hint_"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "hint_tooltip", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip", "_get_tooltip"); @@ -3044,11 +3407,6 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape"); - ADD_GROUP("Size Flags", "size_flags_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); - ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); @@ -3107,6 +3465,7 @@ void Control::_bind_methods() { BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_HEIGHT); BIND_ENUM_CONSTANT(PRESET_MODE_KEEP_SIZE); + BIND_ENUM_CONSTANT(SIZE_SHRINK_BEGIN); BIND_ENUM_CONSTANT(SIZE_FILL); BIND_ENUM_CONSTANT(SIZE_EXPAND); BIND_ENUM_CONSTANT(SIZE_EXPAND_FILL); diff --git a/scene/gui/control.h b/scene/gui/control.h index 962135280f..becb50a118 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -31,12 +31,10 @@ #ifndef CONTROL_H #define CONTROL_H -#include "core/input/shortcut.h" #include "core/math/transform_2d.h" #include "core/object/gdvirtual.gen.inc" #include "core/templates/rid.h" #include "scene/main/canvas_item.h" -#include "scene/main/node.h" #include "scene/main/timer.h" #include "scene/resources/theme.h" @@ -67,12 +65,13 @@ public: }; enum SizeFlags { + SIZE_SHRINK_BEGIN = 0, SIZE_FILL = 1, SIZE_EXPAND = 2, - SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL, - SIZE_SHRINK_CENTER = 4, //ignored by expand or fill - SIZE_SHRINK_END = 8, //ignored by expand or fill + SIZE_SHRINK_CENTER = 4, + SIZE_SHRINK_END = 8, + SIZE_EXPAND_FILL = SIZE_EXPAND | SIZE_FILL, }; enum MouseFilter { @@ -128,6 +127,13 @@ public: PRESET_MODE_KEEP_SIZE }; + enum LayoutMode { + LAYOUT_MODE_POSITION, + LAYOUT_MODE_ANCHORS, + LAYOUT_MODE_CONTAINER, + LAYOUT_MODE_UNCONTROLLED, + }; + enum LayoutDirection { LAYOUT_DIRECTION_INHERITED, LAYOUT_DIRECTION_LOCALE, @@ -230,7 +236,7 @@ private: } data; - static constexpr unsigned properties_managed_by_container_count = 11; + static constexpr unsigned properties_managed_by_container_count = 12; static String properties_managed_by_container[properties_managed_by_container_count]; void _window_find_focus_neighbor(const Vector2 &p_dir, Node *p_at, const Point2 *p_points, real_t p_min, real_t &r_closest_dist, Control **r_closest); @@ -241,6 +247,12 @@ private: void _set_global_position(const Point2 &p_point); void _set_size(const Size2 &p_size); + void _set_layout_mode(LayoutMode p_mode); + LayoutMode _get_layout_mode() const; + + void _set_anchors_layout_preset(int p_preset); + int _get_anchors_layout_preset() const; + void _theme_changed(); void _notify_theme_changed(); @@ -285,9 +297,10 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; + virtual void _validate_property(PropertyInfo &property) const override; + void _notification(int p_notification); static void _bind_methods(); - virtual void _validate_property(PropertyInfo &property) const override; //bind helpers @@ -378,6 +391,7 @@ public: void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true); void set_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); void set_anchors_and_offsets_preset(LayoutPreset p_preset, LayoutPresetMode p_resize_mode = PRESET_MODE_MINSIZE, int p_margin = 0); + void set_grow_direction_preset(LayoutPreset p_preset); void set_anchor(Side p_side, real_t p_anchor, bool p_keep_offset = true, bool p_push_opposite_anchor = true); real_t get_anchor(Side p_side) const; @@ -563,6 +577,7 @@ VARIANT_ENUM_CAST(Control::LayoutPresetMode); VARIANT_ENUM_CAST(Control::MouseFilter); VARIANT_ENUM_CAST(Control::GrowDirection); VARIANT_ENUM_CAST(Control::Anchor); +VARIANT_ENUM_CAST(Control::LayoutMode); VARIANT_ENUM_CAST(Control::LayoutDirection); VARIANT_ENUM_CAST(Control::TextDirection); VARIANT_ENUM_CAST(Control::StructuredTextParser); diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 1cbe3adb3c..e3744eedca 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -33,12 +33,7 @@ #include "core/os/keyboard.h" #include "core/string/print_string.h" #include "core/string/translation.h" -#include "line_edit.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_node.h" -#include "scene/main/window.h" // Only used to check for more modals when dimming the editor. -#endif +#include "scene/gui/line_edit.h" // AcceptDialog @@ -72,21 +67,25 @@ void AcceptDialog::_notification(int p_what) { } } } break; + case NOTIFICATION_THEME_CHANGED: { bg->add_theme_style_override("panel", bg->get_theme_stylebox(SNAME("panel"), SNAME("AcceptDialog"))); } break; + case NOTIFICATION_EXIT_TREE: { if (parent_visible) { parent_visible->disconnect("focus_entered", callable_mp(this, &AcceptDialog::_parent_focused)); parent_visible = nullptr; } } break; + case NOTIFICATION_READY: case NOTIFICATION_WM_SIZE_CHANGED: { if (is_visible()) { _update_child_rects(); } } break; + case NOTIFICATION_WM_CLOSE_REQUEST: { _cancel_pressed(); } break; diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index dad84461f4..79aaf5c511 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -92,26 +92,30 @@ void FileDialog::_theme_changed() { } void FileDialog::_notification(int p_what) { - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (!is_visible()) { - set_process_unhandled_input(false); - } - } - if (p_what == NOTIFICATION_ENTER_TREE) { - dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); - if (vbox->is_layout_rtl()) { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); - } else { - dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); - dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); - } - refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); - show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); - _theme_changed(); - } - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { - update_filters(); + switch (p_what) { + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + set_process_unhandled_input(false); + } + } break; + + case NOTIFICATION_ENTER_TREE: { + dir_up->set_icon(vbox->get_theme_icon(SNAME("parent_folder"), SNAME("FileDialog"))); + if (vbox->is_layout_rtl()) { + dir_prev->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + dir_next->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + } else { + dir_prev->set_icon(vbox->get_theme_icon(SNAME("back_folder"), SNAME("FileDialog"))); + dir_next->set_icon(vbox->get_theme_icon(SNAME("forward_folder"), SNAME("FileDialog"))); + } + refresh->set_icon(vbox->get_theme_icon(SNAME("reload"), SNAME("FileDialog"))); + show_hidden->set_icon(vbox->get_theme_icon(SNAME("toggle_hidden"), SNAME("FileDialog"))); + _theme_changed(); + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: { + update_filters(); + } break; } } diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index d1ac60b325..3bd21f96b2 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -28,8 +28,6 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "scene/gui/container.h" - #include "flow_container.h" struct _LineData { @@ -223,15 +221,41 @@ Size2 FlowContainer::get_minimum_size() const { return minimum; } +Vector<int> FlowContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (!vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> FlowContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void FlowContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { _resort(); update_minimum_size(); } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { queue_sort(); diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index e3ed423ae1..a2da43e071 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -31,7 +31,7 @@ #ifndef FLOW_CONTAINER_H #define FLOW_CONTAINER_H -class Container; +#include "scene/gui/container.h" class FlowContainer : public Container { GDCLASS(FlowContainer, Container); @@ -54,6 +54,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + FlowContainer(bool p_vertical = false); }; diff --git a/scene/gui/gradient_edit.cpp b/scene/gui/gradient_edit.cpp index bec3d58384..0690acbe16 100644 --- a/scene/gui/gradient_edit.cpp +++ b/scene/gui/gradient_edit.cpp @@ -287,84 +287,85 @@ void GradientEdit::gui_input(const Ref<InputEvent> &p_event) { } void GradientEdit::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { - picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (!picker->is_connected("color_changed", callable_mp(this, &GradientEdit::_color_changed))) { + picker->connect("color_changed", callable_mp(this, &GradientEdit::_color_changed)); + } + [[fallthrough]]; } - } + case NOTIFICATION_THEME_CHANGED: { + draw_spacing = BASE_SPACING * get_theme_default_base_scale(); + draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale(); + } break; - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - draw_spacing = BASE_SPACING * get_theme_default_base_scale(); - draw_point_width = BASE_POINT_WIDTH * get_theme_default_base_scale(); - } + case NOTIFICATION_DRAW: { + int w = get_size().x; + int h = get_size().y; - if (p_what == NOTIFICATION_DRAW) { - int w = get_size().x; - int h = get_size().y; + if (w == 0 || h == 0) { + return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size. + } - if (w == 0 || h == 0) { - return; // Safety check. We have division by 'h'. And in any case there is nothing to draw with such size. - } + int total_w = get_size().width - get_size().height - draw_spacing; - int total_w = get_size().width - get_size().height - draw_spacing; + // Draw checker pattern for ramp. + draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true); - // Draw checker pattern for ramp. - draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(0, 0, total_w, h), true); + // Draw color ramp. + gradient_cache->set_points(points); + gradient_cache->set_interpolation_mode(interpolation_mode); + preview_texture->set_gradient(gradient_cache); + draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h)); - // Draw color ramp. + // Draw point markers. + for (int i = 0; i < points.size(); i++) { + Color col = points[i].color.inverted(); + col.a = 0.9; - gradient_cache->set_points(points); - gradient_cache->set_interpolation_mode(interpolation_mode); - preview_texture->set_gradient(gradient_cache); - draw_texture_rect(preview_texture, Rect2(0, 0, total_w, h)); + draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); + Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2); + draw_rect(rect, points[i].color, true); + draw_rect(rect, col, false); + if (grabbed == i) { + rect = rect.grow(-1); + if (has_focus()) { + draw_rect(rect, Color(1, 0, 0, 0.9), false); + } else { + draw_rect(rect, Color(0.6, 0, 0, 0.9), false); + } - // Draw point markers. - for (int i = 0; i < points.size(); i++) { - Color col = points[i].color.inverted(); - col.a = 0.9; - - draw_line(Vector2(points[i].offset * total_w, 0), Vector2(points[i].offset * total_w, h / 2), col); - Rect2 rect = Rect2(points[i].offset * total_w - draw_point_width / 2, h / 2, draw_point_width, h / 2); - draw_rect(rect, points[i].color, true); - draw_rect(rect, col, false); - if (grabbed == i) { - rect = rect.grow(-1); - if (has_focus()) { - draw_rect(rect, Color(1, 0, 0, 0.9), false); - } else { - draw_rect(rect, Color(0.6, 0, 0, 0.9), false); + rect = rect.grow(-1); + draw_rect(rect, col, false); } - - rect = rect.grow(-1); - draw_rect(rect, col, false); } - } - //Draw "button" for color selector - draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true); - if (grabbed != -1) { - //Draw with selection color - draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color); - } else { - //if no color selected draw grey color with 'X' on top. - draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1)); - draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6)); - } + // Draw "button" for color selector. + draw_texture_rect(get_theme_icon(SNAME("GuiMiniCheckerboard"), SNAME("EditorIcons")), Rect2(total_w + draw_spacing, 0, h, h), true); + if (grabbed != -1) { + // Draw with selection color. + draw_rect(Rect2(total_w + draw_spacing, 0, h, h), points[grabbed].color); + } else { + // If no color selected draw grey color with 'X' on top. + draw_rect(Rect2(total_w + draw_spacing, 0, h, h), Color(0.5, 0.5, 0.5, 1)); + draw_line(Vector2(total_w + draw_spacing, 0), Vector2(total_w + draw_spacing + h, h), Color(1, 1, 1, 0.6)); + draw_line(Vector2(total_w + draw_spacing, h), Vector2(total_w + draw_spacing + h, 0), Color(1, 1, 1, 0.6)); + } - // Draw borders around color ramp if in focus. - if (has_focus()) { - draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); - draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); - } - } + // Draw borders around color ramp if in focus. + if (has_focus()) { + draw_line(Vector2(-1, -1), Vector2(total_w + 1, -1), Color(1, 1, 1, 0.6)); + draw_line(Vector2(total_w + 1, -1), Vector2(total_w + 1, h + 1), Color(1, 1, 1, 0.6)); + draw_line(Vector2(total_w + 1, h + 1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); + draw_line(Vector2(-1, -1), Vector2(-1, h + 1), Color(1, 1, 1, 0.6)); + } + } break; - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - if (!is_visible()) { - grabbing = false; - } + case NOTIFICATION_VISIBILITY_CHANGED: { + if (!is_visible()) { + grabbing = false; + } + } break; } } diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 95575a8226..ab21c747cf 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -423,82 +423,86 @@ void GraphEdit::remove_child_notify(Node *p_child) { } void GraphEdit::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { - port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal")); - port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical")); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: { + port_grab_distance_horizontal = get_theme_constant(SNAME("port_grab_distance_horizontal")); + port_grab_distance_vertical = get_theme_constant(SNAME("port_grab_distance_vertical")); + + zoom_minus->set_icon(get_theme_icon(SNAME("minus"))); + zoom_reset->set_icon(get_theme_icon(SNAME("reset"))); + zoom_plus->set_icon(get_theme_icon(SNAME("more"))); + snap_button->set_icon(get_theme_icon(SNAME("snap"))); + minimap_button->set_icon(get_theme_icon(SNAME("minimap"))); + layout_button->set_icon(get_theme_icon(SNAME("layout"))); + + zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale()); + } break; - zoom_minus->set_icon(get_theme_icon(SNAME("minus"))); - zoom_reset->set_icon(get_theme_icon(SNAME("reset"))); - zoom_plus->set_icon(get_theme_icon(SNAME("more"))); - snap_button->set_icon(get_theme_icon(SNAME("snap"))); - minimap_button->set_icon(get_theme_icon(SNAME("minimap"))); - layout_button->set_icon(get_theme_icon(SNAME("layout"))); + case NOTIFICATION_READY: { + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); - zoom_label->set_custom_minimum_size(Size2(48, 0) * get_theme_default_base_scale()); - } - if (p_what == NOTIFICATION_READY) { - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); + h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0); + h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height); + h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); - h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0); - h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); - h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height); - h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width); + v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); + v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); + } break; - v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width); - v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); - v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0); - v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0); - } - if (p_what == NOTIFICATION_DRAW) { - draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size())); + case NOTIFICATION_DRAW: { + draw_style_box(get_theme_stylebox(SNAME("bg")), Rect2(Point2(), get_size())); - if (is_using_snap()) { - //draw grid + if (is_using_snap()) { + // Draw grid. + int snap = get_snap(); - int snap = get_snap(); + Vector2 offset = get_scroll_ofs() / zoom; + Size2 size = get_size() / zoom; - Vector2 offset = get_scroll_ofs() / zoom; - Size2 size = get_size() / zoom; + Point2i from = (offset / float(snap)).floor(); + Point2i len = (size / float(snap)).floor() + Vector2(1, 1); - Point2i from = (offset / float(snap)).floor(); - Point2i len = (size / float(snap)).floor() + Vector2(1, 1); + Color grid_minor = get_theme_color(SNAME("grid_minor")); + Color grid_major = get_theme_color(SNAME("grid_major")); - Color grid_minor = get_theme_color(SNAME("grid_minor")); - Color grid_major = get_theme_color(SNAME("grid_major")); + for (int i = from.x; i < from.x + len.x; i++) { + Color color; - for (int i = from.x; i < from.x + len.x; i++) { - Color color; + if (ABS(i) % 10 == 0) { + color = grid_major; + } else { + color = grid_minor; + } - if (ABS(i) % 10 == 0) { - color = grid_major; - } else { - color = grid_minor; + float base_ofs = i * snap * zoom - offset.x * zoom; + draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color); } - float base_ofs = i * snap * zoom - offset.x * zoom; - draw_line(Vector2(base_ofs, 0), Vector2(base_ofs, get_size().height), color); - } + for (int i = from.y; i < from.y + len.y; i++) { + Color color; - for (int i = from.y; i < from.y + len.y; i++) { - Color color; + if (ABS(i) % 10 == 0) { + color = grid_major; + } else { + color = grid_minor; + } - if (ABS(i) % 10 == 0) { - color = grid_major; - } else { - color = grid_minor; + float base_ofs = i * snap * zoom - offset.y * zoom; + draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color); } - - float base_ofs = i * snap * zoom - offset.y * zoom; - draw_line(Vector2(0, base_ofs), Vector2(get_size().width, base_ofs), color); } - } - } + } break; - if (p_what == NOTIFICATION_RESIZED) { - _update_scroll(); - top_layer->update(); - minimap->update(); + case NOTIFICATION_RESIZED: { + _update_scroll(); + top_layer->update(); + minimap->update(); + } break; } } diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index da973b46f0..b0d1944d6e 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -36,9 +36,7 @@ #include "scene/gui/graph_node.h" #include "scene/gui/label.h" #include "scene/gui/scroll_bar.h" -#include "scene/gui/slider.h" #include "scene/gui/spin_box.h" -#include "scene/gui/texture_rect.h" class GraphEdit; class ViewPanner; diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 30f6cf4a14..ef0ac75cb4 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -31,6 +31,7 @@ #include "graph_node.h" #include "core/string/translation.h" + #ifdef TOOLS_ENABLED #include "graph_edit.h" #endif @@ -959,6 +960,25 @@ bool GraphNode::is_resizable() const { return resizable; } +Vector<int> GraphNode::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> GraphNode::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_EXPAND); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &GraphNode::set_title); ClassDB::bind_method(D_METHOD("get_title"), &GraphNode::get_title); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index b41fc7f5d4..7eb5f27cff 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -182,6 +182,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + bool is_resizing() const { return resizing; } GraphNode(); diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index 465c9dac78..3c1f4bb93b 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -179,11 +179,12 @@ void GridContainer::_notification(int p_what) { col_ofs += s.width + hsep; } } - } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { queue_sort(); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 0cb3249c1d..9585b4d51d 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -855,445 +855,449 @@ static Rect2 _adjust_to_max_size(Size2 p_size, Size2 p_max_size) { } void ItemList::_notification(int p_what) { - if (p_what == NOTIFICATION_RESIZED) { - shape_changed = true; - update(); - } + switch (p_what) { + case NOTIFICATION_RESIZED: { + shape_changed = true; + update(); + } break; + + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_THEME_CHANGED: { + for (int i = 0; i < items.size(); i++) { + _shape(i); + } + shape_changed = true; + update(); + } break; - if ((p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) || (p_what == NOTIFICATION_TRANSLATION_CHANGED) || (p_what == NOTIFICATION_THEME_CHANGED)) { - for (int i = 0; i < items.size(); i++) { - _shape(i); - } - shape_changed = true; - update(); - } + case NOTIFICATION_DRAW: { + Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - if (p_what == NOTIFICATION_DRAW) { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); + int mw = scroll_bar->get_minimum_size().x; + scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw); + scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); + scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP)); + scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM)); - int mw = scroll_bar->get_minimum_size().x; - scroll_bar->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -mw); - scroll_bar->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0); - scroll_bar->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, bg->get_margin(SIDE_TOP)); - scroll_bar->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, -bg->get_margin(SIDE_BOTTOM)); + Size2 size = get_size(); - Size2 size = get_size(); + int width = size.width - bg->get_minimum_size().width; + if (scroll_bar->is_visible()) { + width -= mw; + } - int width = size.width - bg->get_minimum_size().width; - if (scroll_bar->is_visible()) { - width -= mw; - } + draw_style_box(bg, Rect2(Point2(), size)); - draw_style_box(bg, Rect2(Point2(), size)); + int hseparation = get_theme_constant(SNAME("hseparation")); + int vseparation = get_theme_constant(SNAME("vseparation")); + int icon_margin = get_theme_constant(SNAME("icon_margin")); + int line_separation = get_theme_constant(SNAME("line_separation")); + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); - int hseparation = get_theme_constant(SNAME("hseparation")); - int vseparation = get_theme_constant(SNAME("vseparation")); - int icon_margin = get_theme_constant(SNAME("icon_margin")); - int line_separation = get_theme_constant(SNAME("line_separation")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected")); + Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused")); + bool rtl = is_layout_rtl(); - Ref<StyleBox> sbsel = has_focus() ? get_theme_stylebox(SNAME("selected_focus")) : get_theme_stylebox(SNAME("selected")); - Ref<StyleBox> cursor = has_focus() ? get_theme_stylebox(SNAME("cursor")) : get_theme_stylebox(SNAME("cursor_unfocused")); - bool rtl = is_layout_rtl(); + Color guide_color = get_theme_color(SNAME("guide_color")); + Color font_color = get_theme_color(SNAME("font_color")); + Color font_selected_color = get_theme_color(SNAME("font_selected_color")); - Color guide_color = get_theme_color(SNAME("guide_color")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_selected_color = get_theme_color(SNAME("font_selected_color")); + if (has_focus()) { + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true); + draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size)); + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false); + } - if (has_focus()) { - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), true); - draw_style_box(get_theme_stylebox(SNAME("bg_focus")), Rect2(Point2(), size)); - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(), false); - } + if (shape_changed) { + float max_column_width = 0.0; - if (shape_changed) { - float max_column_width = 0.0; + //1- compute item minimum sizes + for (int i = 0; i < items.size(); i++) { + Size2 minsize; + if (items[i].icon.is_valid()) { + if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { + minsize = fixed_icon_size * icon_scale; + } else { + minsize = items[i].get_icon_size() * icon_scale; + } - //1- compute item minimum sizes - for (int i = 0; i < items.size(); i++) { - Size2 minsize; - if (items[i].icon.is_valid()) { - if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { - minsize = fixed_icon_size * icon_scale; - } else { - minsize = items[i].get_icon_size() * icon_scale; + if (!items[i].text.is_empty()) { + if (icon_mode == ICON_MODE_TOP) { + minsize.y += icon_margin; + } else { + minsize.x += icon_margin; + } + } } if (!items[i].text.is_empty()) { - if (icon_mode == ICON_MODE_TOP) { - minsize.y += icon_margin; - } else { - minsize.x += icon_margin; + int max_width = -1; + if (fixed_column_width) { + max_width = fixed_column_width; + } else if (same_column_width) { + max_width = items[i].rect_cache.size.x; } - } - } + items.write[i].text_buf->set_width(max_width); + Size2 s = items[i].text_buf->get_size(); - if (!items[i].text.is_empty()) { - int max_width = -1; - if (fixed_column_width) { - max_width = fixed_column_width; - } else if (same_column_width) { - max_width = items[i].rect_cache.size.x; - } - items.write[i].text_buf->set_width(max_width); - Size2 s = items[i].text_buf->get_size(); + if (icon_mode == ICON_MODE_TOP) { + minsize.x = MAX(minsize.x, s.width); + if (max_text_lines > 0) { + minsize.y += s.height + line_separation * max_text_lines; + } else { + minsize.y += s.height; + } - if (icon_mode == ICON_MODE_TOP) { - minsize.x = MAX(minsize.x, s.width); - if (max_text_lines > 0) { - minsize.y += s.height + line_separation * max_text_lines; } else { - minsize.y += s.height; + minsize.y = MAX(minsize.y, s.height); + minsize.x += s.width; } + } - } else { - minsize.y = MAX(minsize.y, s.height); - minsize.x += s.width; + if (fixed_column_width > 0) { + minsize.x = fixed_column_width; } + max_column_width = MAX(max_column_width, minsize.x); + + // elements need to adapt to the selected size + minsize.y += vseparation; + minsize.x += hseparation; + items.write[i].rect_cache.size = minsize; + items.write[i].min_rect_cache.size = minsize; } - if (fixed_column_width > 0) { - minsize.x = fixed_column_width; + int fit_size = size.x - bg->get_minimum_size().width - mw; + + //2-attempt best fit + current_columns = 0x7FFFFFFF; + if (max_columns > 0) { + current_columns = max_columns; } - max_column_width = MAX(max_column_width, minsize.x); - // elements need to adapt to the selected size - minsize.y += vseparation; - minsize.x += hseparation; - items.write[i].rect_cache.size = minsize; - items.write[i].min_rect_cache.size = minsize; - } + while (true) { + //repeat until all fits + bool all_fit = true; + Vector2 ofs; + int col = 0; + int max_h = 0; + separators.clear(); + for (int i = 0; i < items.size(); i++) { + if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) { + //went past + current_columns = MAX(col, 1); + all_fit = false; + break; + } - int fit_size = size.x - bg->get_minimum_size().width - mw; + if (same_column_width) { + items.write[i].rect_cache.size.x = max_column_width; + } + items.write[i].rect_cache.position = ofs; + max_h = MAX(max_h, items[i].rect_cache.size.y); + ofs.x += items[i].rect_cache.size.x + hseparation; + col++; + if (col == current_columns) { + if (i < items.size() - 1) { + separators.push_back(ofs.y + max_h + vseparation / 2); + } - //2-attempt best fit - current_columns = 0x7FFFFFFF; - if (max_columns > 0) { - current_columns = max_columns; - } + for (int j = i; j >= 0 && col > 0; j--, col--) { + items.write[j].rect_cache.size.y = max_h; + } - while (true) { - //repeat until all fits - bool all_fit = true; - Vector2 ofs; - int col = 0; - int max_h = 0; - separators.clear(); - for (int i = 0; i < items.size(); i++) { - if (current_columns > 1 && items[i].rect_cache.size.width + ofs.x > fit_size) { - //went past - current_columns = MAX(col, 1); - all_fit = false; - break; + ofs.x = 0; + ofs.y += max_h + vseparation; + col = 0; + max_h = 0; + } } - if (same_column_width) { - items.write[i].rect_cache.size.x = max_column_width; + for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { + items.write[j].rect_cache.size.y = max_h; } - items.write[i].rect_cache.position = ofs; - max_h = MAX(max_h, items[i].rect_cache.size.y); - ofs.x += items[i].rect_cache.size.x + hseparation; - col++; - if (col == current_columns) { - if (i < items.size() - 1) { - separators.push_back(ofs.y + max_h + vseparation / 2); - } - for (int j = i; j >= 0 && col > 0; j--, col--) { - items.write[j].rect_cache.size.y = max_h; + if (all_fit) { + float page = MAX(0, size.height - bg->get_minimum_size().height); + float max = MAX(page, ofs.y + max_h); + if (auto_height) { + auto_height_value = ofs.y + max_h + bg->get_minimum_size().height; } + scroll_bar->set_max(max); + scroll_bar->set_page(page); + if (max <= page) { + scroll_bar->set_value(0); + scroll_bar->hide(); + } else { + scroll_bar->show(); - ofs.x = 0; - ofs.y += max_h + vseparation; - col = 0; - max_h = 0; + if (do_autoscroll_to_bottom) { + scroll_bar->set_value(max); + } + } + break; } } - for (int j = items.size() - 1; j >= 0 && col > 0; j--, col--) { - items.write[j].rect_cache.size.y = max_h; - } + update_minimum_size(); + shape_changed = false; + } - if (all_fit) { - float page = MAX(0, size.height - bg->get_minimum_size().height); - float max = MAX(page, ofs.y + max_h); - if (auto_height) { - auto_height_value = ofs.y + max_h + bg->get_minimum_size().height; - } - scroll_bar->set_max(max); - scroll_bar->set_page(page); - if (max <= page) { - scroll_bar->set_value(0); - scroll_bar->hide(); - } else { - scroll_bar->show(); + //ensure_selected_visible needs to be checked before we draw the list. + if (ensure_selected_visible && current >= 0 && current < items.size()) { + Rect2 r = items[current].rect_cache; + int from = scroll_bar->get_value(); + int to = from + scroll_bar->get_page(); - if (do_autoscroll_to_bottom) { - scroll_bar->set_value(max); - } - } - break; + if (r.position.y < from) { + scroll_bar->set_value(r.position.y); + } else if (r.position.y + r.size.y > to) { + scroll_bar->set_value(r.position.y + r.size.y - (to - from)); } } - update_minimum_size(); - shape_changed = false; - } - - //ensure_selected_visible needs to be checked before we draw the list. - if (ensure_selected_visible && current >= 0 && current < items.size()) { - Rect2 r = items[current].rect_cache; - int from = scroll_bar->get_value(); - int to = from + scroll_bar->get_page(); + ensure_selected_visible = false; - if (r.position.y < from) { - scroll_bar->set_value(r.position.y); - } else if (r.position.y + r.size.y > to) { - scroll_bar->set_value(r.position.y + r.size.y - (to - from)); - } - } + Vector2 base_ofs = bg->get_offset(); + base_ofs.y -= int(scroll_bar->get_value()); - ensure_selected_visible = false; + const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there - Vector2 base_ofs = bg->get_offset(); - base_ofs.y -= int(scroll_bar->get_value()); - - const Rect2 clip(-base_ofs, size); // visible frame, don't need to draw outside of there - - int first_item_visible; - { - // do a binary search to find the first item whose rect reaches below clip.position.y - int lo = 0; - int hi = items.size(); - while (lo < hi) { - const int mid = (lo + hi) / 2; - const Rect2 &rcache = items[mid].rect_cache; - if (rcache.position.y + rcache.size.y < clip.position.y) { - lo = mid + 1; - } else { - hi = mid; + int first_item_visible; + { + // do a binary search to find the first item whose rect reaches below clip.position.y + int lo = 0; + int hi = items.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + const Rect2 &rcache = items[mid].rect_cache; + if (rcache.position.y + rcache.size.y < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } } - } - // we might have ended up with column 2, or 3, ..., so let's find the first column - while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) { - lo -= 1; - } - first_item_visible = lo; - } - - for (int i = first_item_visible; i < items.size(); i++) { - Rect2 rcache = items[i].rect_cache; - - if (rcache.position.y > clip.position.y + clip.size.y) { - break; // done + // we might have ended up with column 2, or 3, ..., so let's find the first column + while (lo > 0 && items[lo - 1].rect_cache.position.y == items[lo].rect_cache.position.y) { + lo -= 1; + } + first_item_visible = lo; } - if (!clip.intersects(rcache)) { - continue; - } + for (int i = first_item_visible; i < items.size(); i++) { + Rect2 rcache = items[i].rect_cache; - if (current_columns == 1) { - rcache.size.width = width - rcache.position.x; - } + if (rcache.position.y > clip.position.y + clip.size.y) { + break; // done + } - if (items[i].selected) { - Rect2 r = rcache; - r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; - - if (rtl) { - r.position.x = size.width - r.position.x - r.size.x; + if (!clip.intersects(rcache)) { + continue; } - draw_style_box(sbsel, r); - } - if (items[i].custom_bg.a > 0.001) { - Rect2 r = rcache; - r.position += base_ofs; - - // Size rect to make the align the temperature colors - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; - - if (rtl) { - r.position.x = size.width - r.position.x - r.size.x; + if (current_columns == 1) { + rcache.size.width = width - rcache.position.x; } - draw_rect(r, items[i].custom_bg); - } + if (items[i].selected) { + Rect2 r = rcache; + r.position += base_ofs; + r.position.y -= vseparation / 2; + r.size.y += vseparation; + r.position.x -= hseparation / 2; + r.size.x += hseparation; - Vector2 text_ofs; - if (items[i].icon.is_valid()) { - Size2 icon_size; - //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale; + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; + } - if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { - icon_size = fixed_icon_size * icon_scale; - } else { - icon_size = items[i].get_icon_size() * icon_scale; + draw_style_box(sbsel, r); } + if (items[i].custom_bg.a > 0.001) { + Rect2 r = rcache; + r.position += base_ofs; - Vector2 icon_ofs; + // Size rect to make the align the temperature colors + r.position.y -= vseparation / 2; + r.size.y += vseparation; + r.position.x -= hseparation / 2; + r.size.x += hseparation; - Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs; + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; + } - if (icon_mode == ICON_MODE_TOP) { - pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2); - pos.y += icon_margin; - text_ofs.y = icon_size.height + icon_margin * 2; - } else { - pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2); - text_ofs.x = icon_size.width + icon_margin; + draw_rect(r, items[i].custom_bg); } - Rect2 draw_rect = Rect2(pos, icon_size); + Vector2 text_ofs; + if (items[i].icon.is_valid()) { + Size2 icon_size; + //= _adjust_to_max_size(items[i].get_icon_size(),fixed_icon_size) * icon_scale; - if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { - Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size); - draw_rect.position += adj.position; - draw_rect.size = adj.size; - } + if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { + icon_size = fixed_icon_size * icon_scale; + } else { + icon_size = items[i].get_icon_size() * icon_scale; + } - Color modulate = items[i].icon_modulate; - if (items[i].disabled) { - modulate.a *= 0.5; - } + Vector2 icon_ofs; - // If the icon is transposed, we have to switch the size so that it is drawn correctly - if (items[i].icon_transposed) { - Size2 size_tmp = draw_rect.size; - draw_rect.size.x = size_tmp.y; - draw_rect.size.y = size_tmp.x; - } + Point2 pos = items[i].rect_cache.position + icon_ofs + base_ofs; - Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region); + if (icon_mode == ICON_MODE_TOP) { + pos.x += Math::floor((items[i].rect_cache.size.width - icon_size.width) / 2); + pos.y += icon_margin; + text_ofs.y = icon_size.height + icon_margin * 2; + } else { + pos.y += Math::floor((items[i].rect_cache.size.height - icon_size.height) / 2); + text_ofs.x = icon_size.width + icon_margin; + } - if (rtl) { - draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x; - } - draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed); - } + Rect2 draw_rect = Rect2(pos, icon_size); - if (items[i].tag_icon.is_valid()) { - Point2 draw_pos = items[i].rect_cache.position; - if (rtl) { - draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width(); - } - draw_texture(items[i].tag_icon, draw_pos + base_ofs); - } + if (fixed_icon_size.x > 0 && fixed_icon_size.y > 0) { + Rect2 adj = _adjust_to_max_size(items[i].get_icon_size() * icon_scale, icon_size); + draw_rect.position += adj.position; + draw_rect.size = adj.size; + } - if (!items[i].text.is_empty()) { - int max_len = -1; + Color modulate = items[i].icon_modulate; + if (items[i].disabled) { + modulate.a *= 0.5; + } - Vector2 size2 = items[i].text_buf->get_size(); - if (fixed_column_width) { - max_len = fixed_column_width; - } else if (same_column_width) { - max_len = items[i].rect_cache.size.x; - } else { - max_len = size2.x; - } + // If the icon is transposed, we have to switch the size so that it is drawn correctly + if (items[i].icon_transposed) { + Size2 size_tmp = draw_rect.size; + draw_rect.size.x = size_tmp.y; + draw_rect.size.y = size_tmp.x; + } - Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color); - if (items[i].disabled) { - modulate.a *= 0.5; - } + Rect2 region = (items[i].icon_region.size.x == 0 || items[i].icon_region.size.y == 0) ? Rect2(Vector2(), items[i].icon->get_size()) : Rect2(items[i].icon_region); - if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { - text_ofs += base_ofs; - text_ofs += items[i].rect_cache.position; + if (rtl) { + draw_rect.position.x = size.width - draw_rect.position.x - draw_rect.size.x; + } + draw_texture_rect_region(items[i].icon, draw_rect, region, modulate, items[i].icon_transposed); + } + if (items[i].tag_icon.is_valid()) { + Point2 draw_pos = items[i].rect_cache.position; if (rtl) { - text_ofs.x = size.width - text_ofs.x - max_len; + draw_pos.x = size.width - draw_pos.x - items[i].tag_icon->get_width(); } + draw_texture(items[i].tag_icon, draw_pos + base_ofs); + } - items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER); + if (!items[i].text.is_empty()) { + int max_len = -1; - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + Vector2 size2 = items[i].text_buf->get_size(); + if (fixed_column_width) { + max_len = fixed_column_width; + } else if (same_column_width) { + max_len = items[i].rect_cache.size.x; + } else { + max_len = size2.x; } - items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); - } else { - if (fixed_column_width > 0) { - size2.x = MIN(size2.x, fixed_column_width); + Color modulate = items[i].selected ? font_selected_color : (items[i].custom_fg != Color() ? items[i].custom_fg : font_color); + if (items[i].disabled) { + modulate.a *= 0.5; } - if (icon_mode == ICON_MODE_TOP) { - text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2; - } else { - text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2; - } + if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) { + text_ofs += base_ofs; + text_ofs += items[i].rect_cache.position; - text_ofs += base_ofs; - text_ofs += items[i].rect_cache.position; + if (rtl) { + text_ofs.x = size.width - text_ofs.x - max_len; + } - if (rtl) { - text_ofs.x = size.width - text_ofs.x - max_len; - } + items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_CENTER); - items.write[i].text_buf->set_width(max_len); + if (outline_size > 0 && font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + } - if (rtl) { - items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); } else { - items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT); + if (fixed_column_width > 0) { + size2.x = MIN(size2.x, fixed_column_width); + } + + if (icon_mode == ICON_MODE_TOP) { + text_ofs.x += (items[i].rect_cache.size.width - size2.x) / 2; + } else { + text_ofs.y += (items[i].rect_cache.size.height - size2.y) / 2; + } + + text_ofs += base_ofs; + text_ofs += items[i].rect_cache.position; + + if (rtl) { + text_ofs.x = size.width - text_ofs.x - max_len; + } + + items.write[i].text_buf->set_width(max_len); + + if (rtl) { + items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_RIGHT); + } else { + items.write[i].text_buf->set_alignment(HORIZONTAL_ALIGNMENT_LEFT); + } + + if (outline_size > 0 && font_outline_color.a > 0) { + items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + } + + items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); } + } + + if (select_mode == SELECT_MULTI && i == current) { + Rect2 r = rcache; + r.position += base_ofs; + r.position.y -= vseparation / 2; + r.size.y += vseparation; + r.position.x -= hseparation / 2; + r.size.x += hseparation; - if (outline_size > 0 && font_outline_color.a > 0) { - items[i].text_buf->draw_outline(get_canvas_item(), text_ofs, outline_size, font_outline_color); + if (rtl) { + r.position.x = size.width - r.position.x - r.size.x; } - items[i].text_buf->draw(get_canvas_item(), text_ofs, modulate); + draw_style_box(cursor, r); } } - if (select_mode == SELECT_MULTI && i == current) { - Rect2 r = rcache; - r.position += base_ofs; - r.position.y -= vseparation / 2; - r.size.y += vseparation; - r.position.x -= hseparation / 2; - r.size.x += hseparation; - - if (rtl) { - r.position.x = size.width - r.position.x - r.size.x; + int first_visible_separator = 0; + { + // do a binary search to find the first separator that is below clip_position.y + int lo = 0; + int hi = separators.size(); + while (lo < hi) { + const int mid = (lo + hi) / 2; + if (separators[mid] < clip.position.y) { + lo = mid + 1; + } else { + hi = mid; + } } - - draw_style_box(cursor, r); + first_visible_separator = lo; } - } - int first_visible_separator = 0; - { - // do a binary search to find the first separator that is below clip_position.y - int lo = 0; - int hi = separators.size(); - while (lo < hi) { - const int mid = (lo + hi) / 2; - if (separators[mid] < clip.position.y) { - lo = mid + 1; - } else { - hi = mid; + for (int i = first_visible_separator; i < separators.size(); i++) { + if (separators[i] > clip.position.y + clip.size.y) { + break; // done } - } - first_visible_separator = lo; - } - for (int i = first_visible_separator; i < separators.size(); i++) { - if (separators[i] > clip.position.y + clip.size.y) { - break; // done + const int y = base_ofs.y + separators[i]; + draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color); } - - const int y = base_ofs.y + separators[i]; - draw_line(Vector2(bg->get_margin(SIDE_LEFT), y), Vector2(width, y), guide_color); - } + } break; } } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 852aaaab24..419901d5ea 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -263,168 +263,227 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col } void Label::_notification(int p_what) { - if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { - String new_text = atr(text); - if (new_text == xl_text) { - return; // Nothing new. - } - xl_text = new_text; - if (percent_visible < 1) { - visible_chars = get_total_character_count() * percent_visible; - } - dirty = true; + switch (p_what) { + case NOTIFICATION_TRANSLATION_CHANGED: { + String new_text = atr(text); + if (new_text == xl_text) { + return; // Nothing new. + } + xl_text = new_text; + if (percent_visible < 1) { + visible_chars = get_total_character_count() * percent_visible; + } + dirty = true; - update(); - } + update(); + } break; - if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED) { - update(); - } + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + update(); + } break; - if (p_what == NOTIFICATION_DRAW) { - if (clip) { - RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); - } + case NOTIFICATION_DRAW: { + if (clip) { + RenderingServer::get_singleton()->canvas_item_set_clip(get_canvas_item(), true); + } - if (dirty || font_dirty || lines_dirty) { - _shape(); - } + if (dirty || font_dirty || lines_dirty) { + _shape(); + } - RID ci = get_canvas_item(); - - Size2 string_size; - Size2 size = get_size(); - Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); - Ref<Font> font = get_theme_font(SNAME("font")); - Color font_color = get_theme_color(SNAME("font_color")); - Color font_shadow_color = get_theme_color(SNAME("font_shadow_color")); - Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); - int line_spacing = get_theme_constant(SNAME("line_spacing")); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); - bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); - bool rtl_layout = is_layout_rtl(); - - style->draw(ci, Rect2(Point2(0, 0), get_size())); - - float total_h = 0.0; - int lines_visible = 0; - - // Get number of lines to fit to the height. - for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; - if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { - break; + RID ci = get_canvas_item(); + + Size2 string_size; + Size2 size = get_size(); + Ref<StyleBox> style = get_theme_stylebox(SNAME("normal")); + Ref<Font> font = get_theme_font(SNAME("font")); + Color font_color = get_theme_color(SNAME("font_color")); + Color font_shadow_color = get_theme_color(SNAME("font_shadow_color")); + Point2 shadow_ofs(get_theme_constant(SNAME("shadow_offset_x")), get_theme_constant(SNAME("shadow_offset_y"))); + int line_spacing = get_theme_constant(SNAME("line_spacing")); + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); + int shadow_outline_size = get_theme_constant(SNAME("shadow_outline_size")); + bool rtl = (TS->shaped_text_get_inferred_direction(text_rid) == TextServer::DIRECTION_RTL); + bool rtl_layout = is_layout_rtl(); + + style->draw(ci, Rect2(Point2(0, 0), get_size())); + + float total_h = 0.0; + int lines_visible = 0; + + // Get number of lines to fit to the height. + for (int64_t i = lines_skipped; i < lines_rid.size(); i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + if (total_h > (get_size().height - style->get_minimum_size().height + line_spacing)) { + break; + } + lines_visible++; } - lines_visible++; - } - if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { - lines_visible = max_lines_visible; - } + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } - int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); - bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING); - bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !rtl_layout)); - bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && rtl_layout)); - - // Get real total height. - int total_glyphs = 0; - total_h = 0; - for (int64_t i = lines_skipped; i < last_line; i++) { - total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; - total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); - } - int visible_glyphs = total_glyphs * percent_visible; - int processed_glyphs = 0; - total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); - - int vbegin = 0, vsep = 0; - if (lines_visible > 0) { - switch (vertical_alignment) { - case VERTICAL_ALIGNMENT_TOP: { - // Nothing. - } break; - case VERTICAL_ALIGNMENT_CENTER: { - vbegin = (size.y - (total_h - line_spacing)) / 2; - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_BOTTOM: { - vbegin = size.y - (total_h - line_spacing); - vsep = 0; - - } break; - case VERTICAL_ALIGNMENT_FILL: { - vbegin = 0; - if (lines_visible > 1) { - vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); - } else { + int last_line = MIN(lines_rid.size(), lines_visible + lines_skipped); + bool trim_chars = (visible_chars >= 0) && (visible_chars_behavior == VC_CHARS_AFTER_SHAPING); + bool trim_glyphs_ltr = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_LTR) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && !rtl_layout)); + bool trim_glyphs_rtl = (visible_chars >= 0) && ((visible_chars_behavior == VC_GLYPHS_RTL) || ((visible_chars_behavior == VC_GLYPHS_AUTO) && rtl_layout)); + + // Get real total height. + int total_glyphs = 0; + total_h = 0; + for (int64_t i = lines_skipped; i < last_line; i++) { + total_h += TS->shaped_text_get_size(lines_rid[i]).y + font->get_spacing(TextServer::SPACING_TOP) + font->get_spacing(TextServer::SPACING_BOTTOM) + line_spacing; + total_glyphs += TS->shaped_text_get_glyph_count(lines_rid[i]) + TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); + } + int visible_glyphs = total_glyphs * percent_visible; + int processed_glyphs = 0; + total_h += style->get_margin(SIDE_TOP) + style->get_margin(SIDE_BOTTOM); + + int vbegin = 0, vsep = 0; + if (lines_visible > 0) { + switch (vertical_alignment) { + case VERTICAL_ALIGNMENT_TOP: { + // Nothing. + } break; + case VERTICAL_ALIGNMENT_CENTER: { + vbegin = (size.y - (total_h - line_spacing)) / 2; + vsep = 0; + + } break; + case VERTICAL_ALIGNMENT_BOTTOM: { + vbegin = size.y - (total_h - line_spacing); vsep = 0; - } - } break; + } break; + case VERTICAL_ALIGNMENT_FILL: { + vbegin = 0; + if (lines_visible > 1) { + vsep = (size.y - (total_h - line_spacing)) / (lines_visible - 1); + } else { + vsep = 0; + } + + } break; + } } - } - Vector2 ofs; - ofs.y = style->get_offset().y + vbegin; - for (int i = lines_skipped; i < last_line; i++) { - Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); - ofs.x = 0; - ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP); - switch (horizontal_alignment) { - case HORIZONTAL_ALIGNMENT_FILL: - if (rtl && autowrap_mode != AUTOWRAP_OFF) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - break; - case HORIZONTAL_ALIGNMENT_LEFT: { - if (rtl_layout) { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); - } else { - ofs.x = style->get_offset().x; - } - } break; - case HORIZONTAL_ALIGNMENT_CENTER: { - ofs.x = int(size.width - line_size.width) / 2; - } break; - case HORIZONTAL_ALIGNMENT_RIGHT: { - if (rtl_layout) { - ofs.x = style->get_offset().x; - } else { - ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + Vector2 ofs; + ofs.y = style->get_offset().y + vbegin; + for (int i = lines_skipped; i < last_line; i++) { + Size2 line_size = TS->shaped_text_get_size(lines_rid[i]); + ofs.x = 0; + ofs.y += TS->shaped_text_get_ascent(lines_rid[i]) + font->get_spacing(TextServer::SPACING_TOP); + switch (horizontal_alignment) { + case HORIZONTAL_ALIGNMENT_FILL: + if (rtl && autowrap_mode != AUTOWRAP_OFF) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + break; + case HORIZONTAL_ALIGNMENT_LEFT: { + if (rtl_layout) { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } else { + ofs.x = style->get_offset().x; + } + } break; + case HORIZONTAL_ALIGNMENT_CENTER: { + ofs.x = int(size.width - line_size.width) / 2; + } break; + case HORIZONTAL_ALIGNMENT_RIGHT: { + if (rtl_layout) { + ofs.x = style->get_offset().x; + } else { + ofs.x = int(size.width - style->get_margin(SIDE_RIGHT) - line_size.width); + } + } break; + } + + const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); + int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + + int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]); + int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]); + + const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]); + int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); + + // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps. + int processed_glyphs_ol = processed_glyphs; + if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) { + Vector2 offset = ofs; + // Draw RTL ellipsis string when necessary. + if (rtl && ellipsis_pos >= 0) { + for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += ellipsis_glyphs[gl_idx].advance; + } + } } - } break; - } - const Glyph *glyphs = TS->shaped_text_get_glyphs(lines_rid[i]); - int gl_size = TS->shaped_text_get_glyph_count(lines_rid[i]); + // Draw main text. + for (int j = 0; j < gl_size; j++) { + // Trim when necessary. + if (trim_pos >= 0) { + if (rtl) { + if (j < trim_pos) { + continue; + } + } else { + if (j >= trim_pos) { + break; + } + } + } + for (int k = 0; k < glyphs[j].repeat; k++) { + bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); - int ellipsis_pos = TS->shaped_text_get_ellipsis_pos(lines_rid[i]); - int trim_pos = TS->shaped_text_get_trim_pos(lines_rid[i]); + // Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += glyphs[j].advance; + } + } + // Draw LTR ellipsis string when necessary. + if (!rtl && ellipsis_pos >= 0) { + for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { + for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { + bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + //Draw glyph outlines and shadow. + if (!skip) { + draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + } + processed_glyphs_ol++; + offset.x += ellipsis_glyphs[gl_idx].advance; + } + } + } + } - const Glyph *ellipsis_glyphs = TS->shaped_text_get_ellipsis_glyphs(lines_rid[i]); - int ellipsis_gl_size = TS->shaped_text_get_ellipsis_glyph_count(lines_rid[i]); + // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps. - // Draw outline. Note: Do not merge this into the single loop with the main text, to prevent overlaps. - int processed_glyphs_ol = processed_glyphs; - if ((outline_size > 0 && font_outline_color.a != 0) || (font_shadow_color.a != 0)) { - Vector2 offset = ofs; // Draw RTL ellipsis string when necessary. if (rtl && ellipsis_pos >= 0) { for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); //Draw glyph outlines and shadow. if (!skip) { - draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); } - processed_glyphs_ol++; - offset.x += ellipsis_glyphs[gl_idx].advance; + processed_glyphs++; + ofs.x += ellipsis_glyphs[gl_idx].advance; } } } @@ -444,98 +503,42 @@ void Label::_notification(int p_what) { } } for (int k = 0; k < glyphs[j].repeat; k++) { - bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); // Draw glyph outlines and shadow. if (!skip) { - draw_glyph_outline(glyphs[j], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + draw_glyph(glyphs[j], ci, font_color, ofs); } - processed_glyphs_ol++; - offset.x += glyphs[j].advance; + processed_glyphs++; + ofs.x += glyphs[j].advance; } } // Draw LTR ellipsis string when necessary. if (!rtl && ellipsis_pos >= 0) { for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs_ol >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs_ol < total_glyphs - visible_glyphs)); + bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); //Draw glyph outlines and shadow. if (!skip) { - draw_glyph_outline(ellipsis_glyphs[gl_idx], ci, font_color, font_shadow_color, font_outline_color, shadow_outline_size, outline_size, offset, shadow_ofs); + draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); } - processed_glyphs_ol++; - offset.x += ellipsis_glyphs[gl_idx].advance; + processed_glyphs++; + ofs.x += ellipsis_glyphs[gl_idx].advance; } } } + ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM); } + } break; - // Draw main text. Note: Do not merge this into the single loop with the outline, to prevent overlaps. + case NOTIFICATION_THEME_CHANGED: { + font_dirty = true; + update(); + } break; - // Draw RTL ellipsis string when necessary. - if (rtl && ellipsis_pos >= 0) { - for (int gl_idx = ellipsis_gl_size - 1; gl_idx >= 0; gl_idx--) { - for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); - //Draw glyph outlines and shadow. - if (!skip) { - draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); - } - processed_glyphs++; - ofs.x += ellipsis_glyphs[gl_idx].advance; - } - } - } - - // Draw main text. - for (int j = 0; j < gl_size; j++) { - // Trim when necessary. - if (trim_pos >= 0) { - if (rtl) { - if (j < trim_pos) { - continue; - } - } else { - if (j >= trim_pos) { - break; - } - } - } - for (int k = 0; k < glyphs[j].repeat; k++) { - bool skip = (trim_chars && glyphs[j].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); - - // Draw glyph outlines and shadow. - if (!skip) { - draw_glyph(glyphs[j], ci, font_color, ofs); - } - processed_glyphs++; - ofs.x += glyphs[j].advance; - } - } - // Draw LTR ellipsis string when necessary. - if (!rtl && ellipsis_pos >= 0) { - for (int gl_idx = 0; gl_idx < ellipsis_gl_size; gl_idx++) { - for (int j = 0; j < ellipsis_glyphs[gl_idx].repeat; j++) { - bool skip = (trim_chars && ellipsis_glyphs[gl_idx].end > visible_chars) || (trim_glyphs_ltr && (processed_glyphs >= visible_glyphs)) || (trim_glyphs_rtl && (processed_glyphs < total_glyphs - visible_glyphs)); - //Draw glyph outlines and shadow. - if (!skip) { - draw_glyph(ellipsis_glyphs[gl_idx], ci, font_color, ofs); - } - processed_glyphs++; - ofs.x += ellipsis_glyphs[gl_idx].advance; - } - } - } - ofs.y += TS->shaped_text_get_descent(lines_rid[i]) + vsep + line_spacing + font->get_spacing(TextServer::SPACING_BOTTOM); - } - } - - if (p_what == NOTIFICATION_THEME_CHANGED) { - font_dirty = true; - update(); - } - if (p_what == NOTIFICATION_RESIZED) { - lines_dirty = true; + case NOTIFICATION_RESIZED: { + lines_dirty = true; + } break; } } @@ -924,20 +927,24 @@ void Label::_bind_methods() { BIND_ENUM_CONSTANT(VC_GLYPHS_RTL); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text"); - ADD_GROUP("Locale", ""); - ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_vertical_alignment", "get_vertical_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "is_clipping_text"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uppercase"), "set_uppercase", "is_uppercase"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); + + // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied. ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE, "0,999,1"), "set_lines_skipped", "get_lines_skipped"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE, "-1,999,1"), "set_max_lines_visible", "get_max_lines_visible"); + + ADD_GROUP("Locale", ""); + ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); + ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 3aae3377bc..883eb1a1ba 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -656,31 +656,37 @@ void LineEdit::_notification(int p_what) { } } break; #endif + case NOTIFICATION_RESIZED: { _fit_to_width(); scroll_offset = 0; set_caret_column(get_caret_column()); } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _shape(); update(); } break; + case NOTIFICATION_TRANSLATION_CHANGED: { placeholder_translated = atr(placeholder); _shape(); update(); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; draw_caret = true; update(); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { window_has_focus = false; draw_caret = false; update(); } break; + case NOTIFICATION_DRAW: { if ((!has_focus() && !(menu && menu->has_focus()) && !caret_force_displayed) || !window_has_focus) { draw_caret = false; @@ -923,6 +929,7 @@ void LineEdit::_notification(int p_what) { } } } break; + case NOTIFICATION_FOCUS_ENTER: { if (!caret_force_displayed) { if (caret_blink_enabled) { @@ -942,6 +949,7 @@ void LineEdit::_notification(int p_what) { show_virtual_keyboard(); } break; + case NOTIFICATION_FOCUS_EXIT: { if (caret_blink_enabled && !caret_force_displayed) { caret_blink_timer->stop(); @@ -964,6 +972,7 @@ void LineEdit::_notification(int p_what) { deselect(); } } break; + case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (has_focus()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); @@ -974,10 +983,12 @@ void LineEdit::_notification(int p_what) { update(); } } break; - case Control::NOTIFICATION_DRAG_BEGIN: { + + case NOTIFICATION_DRAG_BEGIN: { drag_action = true; } break; - case Control::NOTIFICATION_DRAG_END: { + + case NOTIFICATION_DRAG_END: { if (is_drag_successful()) { if (selection.drag_attempt) { selection.drag_attempt = false; @@ -1606,7 +1617,7 @@ Size2 LineEdit::get_minimum_size() const { Size2 min_size; // Minimum size of text. - int em_space_size = font->get_char_size('M', 0, font_size).x; + float em_space_size = font->get_char_size('M', 0, font_size).x; min_size.width = get_theme_constant(SNAME("minimum_character_width")) * em_space_size; if (expand_to_text_length) { diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 0ff05faf85..8f40f717c2 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "link_button.h" + #include "core/string/translation.h" void LinkButton::_shape() { @@ -148,18 +149,20 @@ void LinkButton::_notification(int p_what) { case NOTIFICATION_TRANSLATION_CHANGED: { xl_text = atr(text); _shape(); - update_minimum_size(); update(); } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { update(); } break; + case NOTIFICATION_THEME_CHANGED: { _shape(); update_minimum_size(); update(); } break; + case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2 size = get_size(); @@ -230,7 +233,6 @@ void LinkButton::_notification(int p_what) { draw_line(Vector2(0, y), Vector2(width, y), color, text_buf->get_line_underline_thickness()); } } - } break; } } diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 7d302e967d..a455e866b1 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -32,7 +32,6 @@ #define LINKBUTTON_H #include "scene/gui/base_button.h" -#include "scene/resources/bit_map.h" #include "scene/resources/text_line.h" class LinkButton : public BaseButton { diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index 7b696ddb84..fac37a8634 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -65,6 +65,24 @@ Size2 MarginContainer::get_minimum_size() const { return max; } +Vector<int> MarginContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> MarginContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void MarginContainer::_notification(int p_what) { switch (p_what) { case NOTIFICATION_SORT_CHILDREN: { @@ -89,6 +107,7 @@ void MarginContainer::_notification(int p_what) { fit_child_in_rect(c, Rect2(margin_left, margin_top, w, h)); } } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index 3a2f0fa8b3..f8a3c5bb11 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -42,6 +42,9 @@ protected: public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + MarginContainer(); }; diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index a985a9d031..46d8a61ca1 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -136,11 +136,16 @@ int MenuButton::get_item_count() const { void MenuButton::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + popup->set_layout_direction((Window::LayoutDirection)get_layout_direction()); + } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible_in_tree()) { popup->hide(); } } break; + case NOTIFICATION_INTERNAL_PROCESS: { Vector2i mouse_pos = DisplayServer::get_singleton()->mouse_get_position() - mouse_pos_adjusted; MenuButton *menu_btn_other = Object::cast_to<MenuButton>(get_viewport()->gui_find_control(mouse_pos)); diff --git a/scene/gui/nine_patch_rect.cpp b/scene/gui/nine_patch_rect.cpp index 779d1307f5..4f34ece86f 100644 --- a/scene/gui/nine_patch_rect.cpp +++ b/scene/gui/nine_patch_rect.cpp @@ -34,18 +34,20 @@ #include "servers/rendering_server.h" void NinePatchRect::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - if (texture.is_null()) { - return; - } + switch (p_what) { + case NOTIFICATION_DRAW: { + if (texture.is_null()) { + return; + } - Rect2 rect = Rect2(Point2(), get_size()); - Rect2 src_rect = region_rect; + Rect2 rect = Rect2(Point2(), get_size()); + Rect2 src_rect = region_rect; - texture->get_rect_region(rect, src_rect, rect, src_rect); + texture->get_rect_region(rect, src_rect, rect, src_rect); - RID ci = get_canvas_item(); - RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center); + RID ci = get_canvas_item(); + RS::get_singleton()->canvas_item_add_nine_patch(ci, rect, src_rect, texture->get_rid(), Vector2(margin[SIDE_LEFT], margin[SIDE_TOP]), Vector2(margin[SIDE_RIGHT], margin[SIDE_BOTTOM]), RS::NinePatchAxisMode(axis_h), RS::NinePatchAxisMode(axis_v), draw_center); + } break; } } @@ -93,10 +95,6 @@ void NinePatchRect::set_texture(const Ref<Texture2D> &p_tex) { } texture = p_tex; update(); - /* - if (texture.is_valid()) - texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites - */ update_minimum_size(); emit_signal(SceneStringNames::get_singleton()->texture_changed); } diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 9984ab240a..698d74843c 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -91,8 +91,12 @@ void OptionButton::_notification(int p_what) { } arrow->draw(ci, ofs, clr); } break; + case NOTIFICATION_TRANSLATION_CHANGED: - case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + popup->set_layout_direction((Window::LayoutDirection)get_layout_direction()); + [[fallthrough]]; + } case NOTIFICATION_THEME_CHANGED: { if (has_theme_icon(SNAME("arrow"))) { if (is_layout_rtl()) { @@ -104,6 +108,7 @@ void OptionButton::_notification(int p_what) { } } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible_in_tree()) { popup->hide(); @@ -115,6 +120,11 @@ void OptionButton::_notification(int p_what) { bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0] == "popup") { + String property = components[2]; + if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { + return false; + } + bool valid; popup->set(String(p_name).trim_prefix("popup/"), p_value, &valid); @@ -133,6 +143,11 @@ bool OptionButton::_set(const StringName &p_name, const Variant &p_value) { bool OptionButton::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); if (components.size() >= 2 && components[0] == "popup") { + String property = components[2]; + if (property != "text" && property != "icon" && property != "id" && property != "disabled" && property != "separator") { + return false; + } + bool valid; r_ret = popup->get(String(p_name).trim_prefix("popup/"), &valid); return valid; @@ -148,14 +163,6 @@ void OptionButton::_get_property_list(List<PropertyInfo> *p_list) const { pi.usage &= ~(popup->get_item_icon(i).is_null() ? PROPERTY_USAGE_STORAGE : 0); p_list->push_back(pi); - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/checkable", i), PROPERTY_HINT_ENUM, "No,As checkbox,As radio button"); - pi.usage &= ~(!popup->is_item_checkable(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - - pi = PropertyInfo(Variant::BOOL, vformat("popup/item_%d/checked", i)); - pi.usage &= ~(!popup->is_item_checked(i) ? PROPERTY_USAGE_STORAGE : 0); - p_list->push_back(pi); - pi = PropertyInfo(Variant::INT, vformat("popup/item_%d/id", i), PROPERTY_HINT_RANGE, "0,10,1,or_greater"); p_list->push_back(pi); @@ -183,10 +190,13 @@ void OptionButton::pressed() { popup->set_size(Size2(size.width, 0)); // If not triggered by the mouse, start the popup with the checked item selected. - if (popup->get_item_count() > 0 && - ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || - (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept")))) { - popup->set_current_index(current > -1 ? current : 0); + if (popup->get_item_count() > 0) { + if ((get_action_mode() == ActionMode::ACTION_MODE_BUTTON_PRESS && Input::get_singleton()->is_action_just_pressed("ui_accept")) || + (get_action_mode() == ActionMode::ACTION_MODE_BUTTON_RELEASE && Input::get_singleton()->is_action_just_released("ui_accept"))) { + popup->set_current_index(current > -1 ? current : 0); + } else { + popup->scroll_to_item(current > -1 ? current : 0); + } } popup->popup(); @@ -264,7 +274,20 @@ bool OptionButton::is_item_disabled(int p_idx) const { void OptionButton::set_item_count(int p_count) { ERR_FAIL_COND(p_count < 0); + + int count_old = get_item_count(); + if (p_count == count_old) { + return; + } + popup->set_item_count(p_count); + + if (p_count > count_old) { + for (int i = count_old; i < p_count; i++) { + popup->set_item_as_radio_checkable(i, true); + } + } + notify_property_list_changed(); } @@ -294,7 +317,7 @@ void OptionButton::_select(int p_which, bool p_emit) { current = NONE_SELECTED; set_text(""); - set_icon(NULL); + set_icon(nullptr); } else { ERR_FAIL_INDEX(p_which, popup->get_item_count()); diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index 86858fdc78..1ac6cf57ab 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -30,35 +30,16 @@ #include "panel.h" -#include "core/string/print_string.h" - void Panel::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - RID ci = get_canvas_item(); - Ref<StyleBox> style = mode == MODE_BACKGROUND ? get_theme_stylebox(SNAME("panel")) : get_theme_stylebox(SNAME("panel_fg")); - style->draw(ci, Rect2(Point2(), get_size())); + switch (p_what) { + case NOTIFICATION_DRAW: { + RID ci = get_canvas_item(); + Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); + style->draw(ci, Rect2(Point2(), get_size())); + } break; } } -void Panel::set_mode(Mode p_mode) { - mode = p_mode; - update(); -} - -Panel::Mode Panel::get_mode() const { - return mode; -} - -void Panel::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_mode", "mode"), &Panel::set_mode); - ClassDB::bind_method(D_METHOD("get_mode"), &Panel::get_mode); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Background,Foreground"), "set_mode", "get_mode"); - - BIND_ENUM_CONSTANT(MODE_BACKGROUND); - BIND_ENUM_CONSTANT(MODE_FOREGROUND); -} - Panel::Panel() { // Has visible stylebox, so stop by default. set_mouse_filter(MOUSE_FILTER_STOP); diff --git a/scene/gui/panel.h b/scene/gui/panel.h index 37f14c250c..5d2e912680 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -36,26 +36,11 @@ class Panel : public Control { GDCLASS(Panel, Control); -public: - enum Mode { - MODE_BACKGROUND, - MODE_FOREGROUND, - }; - -private: - Mode mode = MODE_BACKGROUND; - protected: void _notification(int p_what); - static void _bind_methods(); public: - void set_mode(Mode p_mode); - Mode get_mode() const; - Panel(); }; -VARIANT_ENUM_CAST(Panel::Mode) - #endif // PANEL_H diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 463ad3c513..fe01712a89 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -60,47 +60,67 @@ Size2 PanelContainer::get_minimum_size() const { return ms; } -void PanelContainer::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - RID ci = get_canvas_item(); - Ref<StyleBox> style; - - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } +Vector<int> PanelContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} - style->draw(ci, Rect2(Point2(), get_size())); - } +Vector<int> PanelContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} - if (p_what == NOTIFICATION_SORT_CHILDREN) { - Ref<StyleBox> style; +void PanelContainer::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_DRAW: { + RID ci = get_canvas_item(); + Ref<StyleBox> style; - if (has_theme_stylebox(SNAME("panel"))) { - style = get_theme_stylebox(SNAME("panel")); - } else { - style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); - } + if (has_theme_stylebox(SNAME("panel"))) { + style = get_theme_stylebox(SNAME("panel")); + } else { + style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); + } - Size2 size = get_size(); - Point2 ofs; - if (style.is_valid()) { - size -= style->get_minimum_size(); - ofs += style->get_offset(); - } + style->draw(ci, Rect2(Point2(), get_size())); + } break; - for (int i = 0; i < get_child_count(); i++) { - Control *c = Object::cast_to<Control>(get_child(i)); - if (!c || !c->is_visible_in_tree()) { - continue; + case NOTIFICATION_SORT_CHILDREN: { + Ref<StyleBox> style; + + if (has_theme_stylebox(SNAME("panel"))) { + style = get_theme_stylebox(SNAME("panel")); + } else { + style = get_theme_stylebox(SNAME("panel"), SNAME("PanelContainer")); } - if (c->is_set_as_top_level()) { - continue; + + Size2 size = get_size(); + Point2 ofs; + if (style.is_valid()) { + size -= style->get_minimum_size(); + ofs += style->get_offset(); } - fit_child_in_rect(c, Rect2(ofs, size)); - } + for (int i = 0; i < get_child_count(); i++) { + Control *c = Object::cast_to<Control>(get_child(i)); + if (!c || !c->is_visible_in_tree()) { + continue; + } + if (c->is_set_as_top_level()) { + continue; + } + + fit_child_in_rect(c, Rect2(ofs, size)); + } + } break; } } diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index a5ff74cebb..8f07ce38eb 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -42,6 +42,9 @@ protected: public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + PanelContainer(); }; diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 7c03fcbb37..4a5dc57e36 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -74,19 +74,19 @@ void Popup::_notification(int p_what) { emit_signal(SNAME("popup_hide")); popped_up = false; } - } break; + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { if (has_focus()) { popped_up = true; } } break; + case NOTIFICATION_EXIT_TREE: { _deinitialize_visible_parents(); } break; - case NOTIFICATION_WM_CLOSE_REQUEST: { - _close_pressed(); - } break; + + case NOTIFICATION_WM_CLOSE_REQUEST: case NOTIFICATION_APPLICATION_FOCUS_OUT: { _close_pressed(); } break; @@ -241,13 +241,20 @@ void PopupPanel::_update_child_rects() { } void PopupPanel::_notification(int p_what) { - if (p_what == NOTIFICATION_THEME_CHANGED) { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); - } else if (p_what == NOTIFICATION_READY || p_what == NOTIFICATION_ENTER_TREE) { - panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); - _update_child_rects(); - } else if (p_what == NOTIFICATION_WM_SIZE_CHANGED) { - _update_child_rects(); + switch (p_what) { + case NOTIFICATION_THEME_CHANGED: { + panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); + } break; + + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_READY: { + panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("panel"), get_class_name())); + _update_child_rects(); + } break; + + case NOTIFICATION_WM_SIZE_CHANGED: { + _update_child_rects(); + } break; } } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 812339dc19..deca1451ee 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -30,6 +30,7 @@ #include "popup_menu.h" +#include "core/config/project_settings.h" #include "core/input/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" @@ -67,7 +68,7 @@ Size2 PopupMenu::_get_contents_minimum_size() const { size.width += items[i].h_ofs; - if (items[i].checkable_type) { + if (items[i].checkable_type && !items[i].separator) { has_check = true; } @@ -108,10 +109,9 @@ Size2 PopupMenu::_get_contents_minimum_size() const { int PopupMenu::_get_item_height(int p_item) const { ERR_FAIL_INDEX_V(p_item, items.size(), 0); - ERR_FAIL_COND_V(p_item < 0, 0); int icon_height = items[p_item].get_icon_size().height; - if (items[p_item].checkable_type) { + if (items[p_item].checkable_type && !items[p_item].separator) { icon_height = MAX(icon_height, MAX(get_theme_icon(SNAME("checked"))->get_height(), get_theme_icon(SNAME("radio_checked"))->get_height())); } @@ -141,23 +141,6 @@ int PopupMenu::_get_items_total_height() const { return items_total_height - vsep; } -void PopupMenu::_scroll_to_item(int p_item) { - ERR_FAIL_INDEX(p_item, items.size()); - ERR_FAIL_COND(p_item < 0); - - // Scroll item into view (upwards) - if (items[p_item]._ofs_cache < -control->get_position().y) { - int amnt_over = items[p_item]._ofs_cache + control->get_position().y; - scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over); - } - - // Scroll item into view (downwards) - if (items[p_item]._ofs_cache + items[p_item]._height_cache > -control->get_position().y + scroll_container->get_size().height) { - int amnt_over = items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y - scroll_container->get_size().height; - scroll_container->set_v_scroll(scroll_container->get_v_scroll() + amnt_over); - } -} - int PopupMenu::_get_mouse_over(const Point2 &p_over) const { if (p_over.x < 0 || p_over.x >= get_size().width) { return -1; @@ -203,15 +186,17 @@ void PopupMenu::_activate_submenu(int p_over) { float scroll_offset = control->get_position().y; - Point2 submenu_pos; + submenu_popup->set_as_minsize(); // Shrink the popup size to its contents. Size2 submenu_size = submenu_popup->get_size(); + + Point2 submenu_pos; if (control->is_layout_rtl()) { submenu_pos = this_pos + Point2(-submenu_size.width, items[p_over]._ofs_cache + scroll_offset); } else { submenu_pos = this_pos + Point2(this_rect.size.width, items[p_over]._ofs_cache + scroll_offset); } - // Fix pos if going outside parent rect + // Fix pos if going outside parent rect. if (submenu_pos.x < get_parent_rect().position.x) { submenu_pos.x = this_pos.x + submenu_size.width; } @@ -222,7 +207,6 @@ void PopupMenu::_activate_submenu(int p_over) { submenu_popup->set_close_on_parent_focus(false); submenu_popup->set_position(submenu_pos); - submenu_popup->set_as_minsize(); // Shrink the popup size to its contents. PopupMenu *submenu_pum = Object::cast_to<PopupMenu>(submenu_popup); if (!submenu_pum) { @@ -275,7 +259,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); - _scroll_to_item(i); + scroll_to_item(i); control->update(); set_input_as_handled(); match_found = true; @@ -289,7 +273,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); - _scroll_to_item(i); + scroll_to_item(i); control->update(); set_input_as_handled(); break; @@ -307,7 +291,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); - _scroll_to_item(i); + scroll_to_item(i); control->update(); set_input_as_handled(); match_found = true; @@ -321,7 +305,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (!items[i].separator && !items[i].disabled) { mouse_over = i; emit_signal(SNAME("id_focused"), i); - _scroll_to_item(i); + scroll_to_item(i); control->update(); set_input_as_handled(); break; @@ -471,7 +455,7 @@ void PopupMenu::gui_input(const Ref<InputEvent> &p_event) { if (items[i].text.findn(search_string) == 0) { mouse_over = i; emit_signal(SNAME("id_focused"), i); - _scroll_to_item(i); + scroll_to_item(i); control->update(); set_input_as_handled(); break; @@ -495,7 +479,7 @@ void PopupMenu::_draw_items() { bool rtl = control->is_layout_rtl(); Ref<StyleBox> style = get_theme_stylebox(SNAME("panel")); Ref<StyleBox> hover = get_theme_stylebox(SNAME("hover")); - // In Item::checkable_type enum order (less the non-checkable member) + // In Item::checkable_type enum order (less the non-checkable member). Ref<Texture2D> check[] = { get_theme_icon(SNAME("checked")), get_theme_icon(SNAME("radio_checked")) }; Ref<Texture2D> uncheck[] = { get_theme_icon(SNAME("unchecked")), get_theme_icon(SNAME("radio_unchecked")) }; Ref<Texture2D> submenu; @@ -524,6 +508,10 @@ void PopupMenu::_draw_items() { float icon_ofs = 0.0; bool has_check = false; for (int i = 0; i < items.size(); i++) { + if (items[i].separator) { + continue; + } + icon_ofs = MAX(items[i].get_icon_size().width, icon_ofs); if (items[i].checkable_type) { @@ -567,29 +555,33 @@ void PopupMenu::_draw_items() { if (items[i].separator) { int sep_h = separator->get_center_size().height + separator->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); - if (!text.is_empty()) { - int text_size = items[i].text_buf->get_size().width; - int text_center = display_width / 2; - int text_left = text_center - text_size / 2; - int text_right = text_center + text_size / 2; - if (text_left > item_ofs.x) { - labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, text_left - item_ofs.x), sep_h))); + if (!text.is_empty() || !items[i].icon.is_null()) { + int content_size = items[i].text_buf->get_size().width; + if (!items[i].icon.is_null()) { + content_size += icon_size.width + hseparation; + } + + int content_center = display_width / 2; + int content_left = content_center - content_size / 2; + int content_right = content_center + content_size / 2; + if (content_left > item_ofs.x) { + labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); } - if (text_right < display_width) { - labeled_separator_right->draw(ci, Rect2(Point2(text_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - text_right), sep_h))); + if (content_right < display_width) { + labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); } } else { separator->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } } - Color icon_color(1, 1, 1, items[i].disabled ? 0.5 : 1); + Color icon_color(1, 1, 1, items[i].disabled && !items[i].separator ? 0.5 : 1); // For non-separator items, add some padding for the content. item_ofs.x += item_start_padding; // Checkboxes - if (items[i].checkable_type) { + if (items[i].checkable_type && !items[i].separator) { Texture2D *icon = (items[i].checked ? check[items[i].checkable_type - 1] : uncheck[items[i].checkable_type - 1]).ptr(); if (rtl) { icon->draw(ci, Size2(control->get_size().width - item_ofs.x - icon->get_width(), item_ofs.y) + Point2(0, Math::floor((h - icon->get_height()) / 2.0)), icon_color); @@ -598,16 +590,28 @@ void PopupMenu::_draw_items() { } } + int separator_ofs = (display_width - items[i].text_buf->get_size().width) / 2; + // Icon if (!items[i].icon.is_null()) { - if (rtl) { - items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + if (items[i].separator) { + separator_ofs -= (icon_size.width + hseparation) / 2; + + if (rtl) { + items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } else { + items[i].icon->draw(ci, item_ofs + Size2(separator_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } } else { - items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + if (rtl) { + items[i].icon->draw(ci, Size2(control->get_size().width - item_ofs.x - check_ofs - icon_size.width, item_ofs.y) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } else { + items[i].icon->draw(ci, item_ofs + Size2(check_ofs, 0) + Point2(0, Math::floor((h - icon_size.height) / 2.0)), icon_color); + } } } - // Submenu arrow on right hand side + // Submenu arrow on right hand side. if (!items[i].submenu.is_empty()) { if (rtl) { submenu->draw(ci, Point2(scroll_width + style->get_margin(SIDE_LEFT) + item_end_padding, item_ofs.y + Math::floor(h - submenu->get_height()) / 2), icon_color); @@ -621,8 +625,11 @@ void PopupMenu::_draw_items() { int outline_size = get_theme_constant(SNAME("outline_size")); if (items[i].separator) { if (!text.is_empty()) { - int center = (display_width - items[i].text_buf->get_size().width) / 2; - Vector2 text_pos = Point2(center, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); + Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); + if (!rtl && !items[i].icon.is_null()) { + text_pos.x += icon_size.width + hseparation; + } + if (outline_size > 0 && font_outline_color.a > 0) { items[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); } @@ -659,7 +666,7 @@ void PopupMenu::_draw_items() { items[i].accel_text_buf->draw(ci, text_pos, i == mouse_over ? font_hover_color : font_accelerator_color); } - // Cache the item vertical offset from the first item and the height + // Cache the item vertical offset from the first item and the height. items.write[i]._ofs_cache = ofs.y; items.write[i]._height_cache = h; @@ -724,11 +731,12 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_ENTER_TREE: { PopupMenu *pm = Object::cast_to<PopupMenu>(get_parent()); if (pm) { - // Inherit submenu's popup delay time from parent menu + // Inherit submenu's popup delay time from parent menu. float pm_delay = pm->get_submenu_popup_delay(); set_submenu_popup_delay(pm_delay); } } break; + case NOTIFICATION_THEME_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { @@ -741,23 +749,25 @@ void PopupMenu::_notification(int p_what) { child_controls_changed(); control->update(); } break; + case NOTIFICATION_WM_MOUSE_ENTER: { grab_focus(); } break; + case NOTIFICATION_WM_MOUSE_EXIT: { if (mouse_over >= 0 && (items[mouse_over].submenu.is_empty() || submenu_over != -1)) { mouse_over = -1; control->update(); } } break; + case NOTIFICATION_POST_POPUP: { initial_button_mask = Input::get_singleton()->get_mouse_button_mask(); during_grabbed_click = (bool)initial_button_mask; } break; - case NOTIFICATION_WM_SIZE_CHANGED: { - } break; + case NOTIFICATION_INTERNAL_PROCESS: { - //only used when using operating system windows + // Only used when using operating system windows. if (!is_embedded() && autohide_areas.size()) { Point2 mouse_pos = DisplayServer::get_singleton()->mouse_get_position(); mouse_pos -= get_position(); @@ -770,6 +780,7 @@ void PopupMenu::_notification(int p_what) { } } } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (!is_visible()) { if (mouse_over >= 0) { @@ -819,7 +830,7 @@ void PopupMenu::_notification(int p_what) { #define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \ item.text = p_label; \ item.xl_text = atr(p_label); \ - item.id = p_id == -1 ? items.size() - 1 : p_id; \ + item.id = p_id == -1 ? items.size() : p_id; \ item.accel = p_accel; void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) { @@ -901,7 +912,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int _ref_shortcut(p_shortcut); \ item.text = p_shortcut->get_name(); \ item.xl_text = atr(item.text); \ - item.id = p_id == -1 ? items.size() - 1 : p_id; \ + item.id = p_id == -1 ? items.size() : p_id; \ item.shortcut = p_shortcut; \ item.shortcut_is_global = p_global; @@ -970,7 +981,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, Item item; item.text = p_label; item.xl_text = atr(p_label); - item.id = p_id == -1 ? items.size() - 1 : p_id; + item.id = p_id == -1 ? items.size() : p_id; item.submenu = p_submenu; items.push_back(item); _shape_item(items.size() - 1); @@ -1281,7 +1292,7 @@ bool PopupMenu::is_item_shortcut_disabled(int p_idx) const { void PopupMenu::set_current_index(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); mouse_over = p_idx; - _scroll_to_item(mouse_over); + scroll_to_item(mouse_over); control->update(); } @@ -1309,6 +1320,20 @@ int PopupMenu::get_item_count() const { return items.size(); } +void PopupMenu::scroll_to_item(int p_item) { + ERR_FAIL_INDEX(p_item, items.size()); + + // Scroll item into view (upwards). + if (items[p_item]._ofs_cache - scroll_container->get_v_scroll() < -control->get_position().y) { + scroll_container->set_v_scroll(items[p_item]._ofs_cache + control->get_position().y); + } + + // Scroll item into view (downwards). + if (items[p_item]._ofs_cache + items[p_item]._height_cache - scroll_container->get_v_scroll() > -control->get_position().y + scroll_container->get_size().height) { + scroll_container->set_v_scroll(items[p_item]._ofs_cache + items[p_item]._height_cache + control->get_position().y); + } +} + bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) { Key code = Key::NONE; Ref<InputEventKey> k = p_event; @@ -1582,7 +1607,7 @@ bool PopupMenu::_set(const StringName &p_name, const Variant &p_value) { } else if (property == "id") { set_item_id(item_index, p_value); return true; - } else if (components[1] == "disabled") { + } else if (property == "disabled") { set_item_disabled(item_index, p_value); return true; } else if (property == "separator") { @@ -1655,7 +1680,7 @@ bool PopupMenu::_get(const StringName &p_name, Variant &r_ret) const { } else if (property == "id") { r_ret = get_item_id(item_index); return true; - } else if (components[1] == "disabled") { + } else if (property == "disabled") { r_ret = is_item_disabled(item_index); return true; } else if (property == "separator") { @@ -1761,6 +1786,8 @@ void PopupMenu::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_count", "count"), &PopupMenu::set_item_count); ClassDB::bind_method(D_METHOD("get_item_count"), &PopupMenu::get_item_count); + ClassDB::bind_method(D_METHOD("scroll_to_item", "index"), &PopupMenu::scroll_to_item); + ClassDB::bind_method(D_METHOD("remove_item", "index"), &PopupMenu::remove_item); ClassDB::bind_method(D_METHOD("add_separator", "label", "id"), &PopupMenu::add_separator, DEFVAL(String()), DEFVAL(-1)); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index 7c2212d82d..5ce55209d4 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -103,7 +103,6 @@ class PopupMenu : public Popup { int _get_item_height(int p_item) const; int _get_items_total_height() const; - void _scroll_to_item(int p_item); void _shape_item(int p_item); @@ -218,6 +217,8 @@ public: void set_item_count(int p_count); int get_item_count() const; + void scroll_to_item(int p_item); + bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false); void activate_item(int p_item); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index c20fb0d7a8..20b3513375 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "progress_bar.h" + #include "scene/resources/text_line.h" Size2 ProgressBar::get_minimum_size() const { @@ -52,36 +53,38 @@ Size2 ProgressBar::get_minimum_size() const { } void ProgressBar::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); - Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); - Ref<Font> font = get_theme_font(SNAME("font")); - int font_size = get_theme_font_size(SNAME("font_size")); - Color font_color = get_theme_color(SNAME("font_color")); + switch (p_what) { + case NOTIFICATION_DRAW: { + Ref<StyleBox> bg = get_theme_stylebox(SNAME("bg")); + Ref<StyleBox> fg = get_theme_stylebox(SNAME("fg")); + Ref<Font> font = get_theme_font(SNAME("font")); + int font_size = get_theme_font_size(SNAME("font_size")); + Color font_color = get_theme_color(SNAME("font_color")); - draw_style_box(bg, Rect2(Point2(), get_size())); - float r = get_as_ratio(); - int mp = fg->get_minimum_size().width; - int p = r * (get_size().width - mp); - if (p > 0) { - if (is_layout_rtl()) { - draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height))); - } else { - draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + draw_style_box(bg, Rect2(Point2(), get_size())); + float r = get_as_ratio(); + int mp = fg->get_minimum_size().width; + int p = r * (get_size().width - mp); + if (p > 0) { + if (is_layout_rtl()) { + draw_style_box(fg, Rect2(Point2(p, 0), Size2(fg->get_minimum_size().width, get_size().height))); + } else { + draw_style_box(fg, Rect2(Point2(0, 0), Size2(p + fg->get_minimum_size().width, get_size().height))); + } } - } - if (percent_visible) { - String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); - TextLine tl = TextLine(txt, font, font_size); - Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); - if (outline_size > 0 && font_outline_color.a > 0) { - tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color); + if (percent_visible) { + String txt = TS->format_number(itos(int(get_as_ratio() * 100))) + TS->percent_sign(); + TextLine tl = TextLine(txt, font, font_size); + Vector2 text_pos = (Point2(get_size().width - tl.get_size().x, get_size().height - tl.get_size().y) / 2).round(); + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); + if (outline_size > 0 && font_outline_color.a > 0) { + tl.draw_outline(get_canvas_item(), text_pos, outline_size, font_outline_color); + } + tl.draw(get_canvas_item(), text_pos, font_color); } - tl.draw(get_canvas_item(), text_pos, font_color); - } + } break; } } diff --git a/scene/gui/reference_rect.cpp b/scene/gui/reference_rect.cpp index e2a0d568a1..ed79da5c22 100644 --- a/scene/gui/reference_rect.cpp +++ b/scene/gui/reference_rect.cpp @@ -33,13 +33,15 @@ #include "core/config/engine.h" void ReferenceRect::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - if (!is_inside_tree()) { - return; - } - if (Engine::get_singleton()->is_editor_hint() || !editor_only) { - draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width); - } + switch (p_what) { + case NOTIFICATION_DRAW: { + if (!is_inside_tree()) { + return; + } + if (Engine::get_singleton()->is_editor_hint() || !editor_only) { + draw_rect(Rect2(Point2(), get_size()), border_color, false, border_width); + } + } break; } } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 4865b9770e..dd07831b83 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -33,6 +33,7 @@ #include "core/math/math_defs.h" #include "core/os/keyboard.h" #include "core/os/os.h" +#include "label.h" #include "scene/scene_string_names.h" #include "servers/display_server.h" @@ -513,7 +514,7 @@ void RichTextLabel::_shape_line(ItemFrame *p_frame, int p_line, const Ref<Font> } break; case ITEM_IMAGE: { ItemImage *img = (ItemImage *)it; - l.text_buf->add_object((uint64_t)it, img->image->get_size(), img->inline_align, 1); + l.text_buf->add_object((uint64_t)it, img->size, img->inline_align, 1); text += String::chr(0xfffc); l.char_count++; remaining_characters--; @@ -1501,15 +1502,17 @@ void RichTextLabel::_notification(int p_what) { update(); } } break; + case NOTIFICATION_RESIZED: { main->first_resized_line = 0; //invalidate ALL update(); - } break; + case NOTIFICATION_THEME_CHANGED: { main->first_invalid_font_line = 0; //invalidate ALL update(); } break; + case NOTIFICATION_ENTER_TREE: { if (!text.is_empty()) { set_text(text); @@ -1518,11 +1521,13 @@ void RichTextLabel::_notification(int p_what) { main->first_invalid_line = 0; //invalidate ALL update(); } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { main->first_invalid_line = 0; //invalidate ALL update(); } break; + case NOTIFICATION_DRAW: { _validate_line_caches(main); _update_scroll(); @@ -1577,6 +1582,7 @@ void RichTextLabel::_notification(int p_what) { from_line++; } } break; + case NOTIFICATION_INTERNAL_PROCESS: { if (is_visible_in_tree()) { double dt = get_process_delta_time(); @@ -1584,12 +1590,17 @@ void RichTextLabel::_notification(int p_what) { update(); } } break; + case NOTIFICATION_FOCUS_EXIT: { if (deselect_on_focus_loss_enabled) { selection.active = false; update(); } } break; + + case NOTIFICATION_DRAG_END: { + selection.drag_attempt = false; + } break; } } @@ -1650,6 +1661,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { int c_index = 0; bool outside; + selection.drag_attempt = false; + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); if (c_item != nullptr) { if (selection.enabled) { @@ -1660,17 +1673,22 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { // Erase previous selection. if (selection.active) { - selection.from_frame = nullptr; - selection.from_line = 0; - selection.from_item = nullptr; - selection.from_char = 0; - selection.to_frame = nullptr; - selection.to_line = 0; - selection.to_item = nullptr; - selection.to_char = 0; - selection.active = false; - - update(); + if (_is_click_inside_selection()) { + selection.drag_attempt = true; + selection.click_item = nullptr; + } else { + selection.from_frame = nullptr; + selection.from_line = 0; + selection.from_item = nullptr; + selection.from_char = 0; + selection.to_frame = nullptr; + selection.to_line = 0; + selection.to_item = nullptr; + selection.to_char = 0; + selection.active = false; + + update(); + } } } } @@ -1683,6 +1701,8 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { int c_index = 0; bool outside; + selection.drag_attempt = false; + _find_click(main, b->get_position(), &c_frame, &c_line, &c_item, &c_index, &outside); if (c_frame) { @@ -1714,6 +1734,22 @@ void RichTextLabel::gui_input(const Ref<InputEvent> &p_event) { DisplayServer::get_singleton()->clipboard_set_primary(get_selected_text()); } selection.click_item = nullptr; + if (selection.drag_attempt) { + selection.drag_attempt = false; + if (_is_click_inside_selection()) { + selection.from_frame = nullptr; + selection.from_line = 0; + selection.from_item = nullptr; + selection.from_char = 0; + selection.to_frame = nullptr; + selection.to_line = 0; + selection.to_item = nullptr; + selection.to_char = 0; + selection.active = false; + + update(); + } + } if (!b->is_double_click() && !scroll_updated) { Item *c_item = nullptr; @@ -3689,7 +3725,7 @@ void RichTextLabel::scroll_to_line(int p_line) { if ((line_count <= p_line) && (line_count + main->lines[i].text_buf->get_line_count() >= p_line)) { float line_offset = 0.f; for (int j = 0; j < p_line - line_count; j++) { - line_offset += main->lines[i].text_buf->get_line_size(j).y; + line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation")); } vscroll->set_value(main->lines[i].offset.y + line_offset); return; @@ -3698,6 +3734,28 @@ void RichTextLabel::scroll_to_line(int p_line) { } } +float RichTextLabel::get_line_offset(int p_line) { + int line_count = 0; + for (int i = 0; i < main->lines.size(); i++) { + if ((line_count <= p_line) && (p_line <= line_count + main->lines[i].text_buf->get_line_count())) { + float line_offset = 0.f; + for (int j = 0; j < p_line - line_count; j++) { + line_offset += main->lines[i].text_buf->get_line_size(j).y + get_theme_constant(SNAME("line_separation")); + } + return main->lines[i].offset.y + line_offset; + } + line_count += main->lines[i].text_buf->get_line_count(); + } + return 0; +} + +float RichTextLabel::get_paragraph_offset(int p_paragraph) { + if (0 <= p_paragraph && p_paragraph < main->lines.size()) { + return main->lines[p_paragraph].offset.y; + } + return 0; +} + int RichTextLabel::get_line_count() const { int line_count = 0; for (int i = 0; i < main->lines.size(); i++) { @@ -3734,6 +3792,29 @@ void RichTextLabel::set_deselect_on_focus_loss_enabled(const bool p_enabled) { } } +Variant RichTextLabel::get_drag_data(const Point2 &p_point) { + if (selection.drag_attempt && selection.enabled) { + String t = get_selected_text(); + Label *l = memnew(Label); + l->set_text(t); + set_drag_preview(l); + return t; + } + + return Variant(); +} + +bool RichTextLabel::_is_click_inside_selection() const { + if (selection.active && selection.enabled && selection.click_frame && selection.from_frame && selection.to_frame) { + const Line &l_click = selection.click_frame->lines[selection.click_line]; + const Line &l_from = selection.from_frame->lines[selection.from_line]; + const Line &l_to = selection.to_frame->lines[selection.to_line]; + return (l_click.char_offset + selection.click_char >= l_from.char_offset + selection.from_char) && (l_click.char_offset + selection.click_char <= l_to.char_offset + selection.to_char); + } else { + return false; + } +} + bool RichTextLabel::_search_table(ItemTable *p_table, List<Item *>::Element *p_from, const String &p_string, bool p_reverse_search) { List<Item *>::Element *E = p_from; while (E != nullptr) { @@ -3992,7 +4073,7 @@ int RichTextLabel::get_selection_to() const { void RichTextLabel::set_text(const String &p_bbcode) { text = p_bbcode; - if (is_inside_tree() && use_bbcode) { + if (use_bbcode) { parse_bbcode(p_bbcode); } else { // raw text clear(); @@ -4157,6 +4238,14 @@ int RichTextLabel::get_content_height() const { return total_height; } +int RichTextLabel::get_content_width() const { + int total_width = 0; + for (int i = 0; i < main->lines.size(); i++) { + total_width = MAX(total_width, main->lines[i].offset.x + main->lines[i].text_buf->get_size().x); + } + return total_width; +} + #ifndef DISABLE_DEPRECATED // People will be very angry, if their texts get erased, because of #39148. (3.x -> 4.0) // Although some people may not used bbcode_text, so we only overwrite, if bbcode_text is not empty. @@ -4267,6 +4356,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_percent_visible", "percent_visible"), &RichTextLabel::set_percent_visible); ClassDB::bind_method(D_METHOD("get_percent_visible"), &RichTextLabel::get_percent_visible); + ClassDB::bind_method(D_METHOD("get_character_line", "character"), &RichTextLabel::get_character_line); + ClassDB::bind_method(D_METHOD("get_character_paragraph", "character"), &RichTextLabel::get_character_paragraph); ClassDB::bind_method(D_METHOD("get_total_character_count"), &RichTextLabel::get_total_character_count); ClassDB::bind_method(D_METHOD("set_use_bbcode", "enable"), &RichTextLabel::set_use_bbcode); @@ -4279,6 +4370,10 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_visible_paragraph_count"), &RichTextLabel::get_visible_paragraph_count); ClassDB::bind_method(D_METHOD("get_content_height"), &RichTextLabel::get_content_height); + ClassDB::bind_method(D_METHOD("get_content_width"), &RichTextLabel::get_content_width); + + ClassDB::bind_method(D_METHOD("get_line_offset", "line"), &RichTextLabel::get_line_offset); + ClassDB::bind_method(D_METHOD("get_paragraph_offset", "paragraph"), &RichTextLabel::get_paragraph_offset); ClassDB::bind_method(D_METHOD("parse_expressions_for_values", "expressions"), &RichTextLabel::parse_expressions_for_values); @@ -4286,33 +4381,30 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_effects"), &RichTextLabel::get_effects); ClassDB::bind_method(D_METHOD("install_effect", "effect"), &RichTextLabel::install_effect); - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); - - ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); + // Note: set "bbcode_enabled" first, to avoid unnecessery "text" resets. + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_size", PROPERTY_HINT_RANGE, "0,24,1"), "set_tab_size", "get_tab_size"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, vformat("%s/%s:%s", Variant::OBJECT, PROPERTY_HINT_RESOURCE_TYPE, "RichTextEffect"), (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "meta_underlined"), "set_meta_underline", "is_meta_underlined"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); + + // Note: "visible_characters" and "percent_visible" should be set after "text" to be correctly applied. + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters", PROPERTY_HINT_RANGE, "-1,128000,1"), "set_visible_characters", "get_visible_characters"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "visible_characters_behavior", PROPERTY_HINT_ENUM, "Characters Before Shaping,Characters After Shaping,Glyphs (Layout Direction),Glyphs (Left-to-Right),Glyphs (Right-to-Left)"), "set_visible_characters_behavior", "get_visible_characters_behavior"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "percent_visible", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_percent_visible", "get_percent_visible"); + ADD_GROUP("Locale", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); - ADD_GROUP("Structured Text", "structured_text_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); @@ -4401,6 +4493,36 @@ int RichTextLabel::get_visible_characters() const { return visible_characters; } +int RichTextLabel::get_character_line(int p_char) { + int line_count = 0; + for (int i = 0; i < main->lines.size(); i++) { + if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) { + for (int j = 0; j < main->lines[i].text_buf->get_line_count(); j++) { + Vector2i range = main->lines[i].text_buf->get_line_range(j); + if (main->lines[i].char_offset + range.x < p_char && p_char <= main->lines[i].char_offset + range.y) { + return line_count; + } + line_count++; + } + } else { + line_count += main->lines[i].text_buf->get_line_count(); + } + } + return -1; +} + +int RichTextLabel::get_character_paragraph(int p_char) { + int para_count = 0; + for (int i = 0; i < main->lines.size(); i++) { + if (main->lines[i].char_offset < p_char && p_char <= main->lines[i].char_offset + main->lines[i].char_count) { + return para_count; + } else { + para_count++; + } + } + return -1; +} + int RichTextLabel::get_total_character_count() const { // Note: Do not use line buffer "char_count", it includes only visible characters. int tc = 0; diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index e79244f2e4..53c2046c8f 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -407,6 +407,7 @@ private: bool active = false; // anything selected? i.e. from, to, etc. valid? bool enabled = false; // allow selections? + bool drag_attempt = false; }; Selection selection; @@ -416,6 +417,7 @@ private: float percent_visible = 1.0; VisibleCharactersBehavior visible_chars_behavior = VC_CHARS_BEFORE_SHAPING; + bool _is_click_inside_selection() const; void _find_click(ItemFrame *p_frame, const Point2i &p_click, ItemFrame **r_click_frame = nullptr, int *r_click_line = nullptr, Item **r_click_item = nullptr, int *r_click_char = nullptr, bool *r_outside = nullptr); String _get_line_text(ItemFrame *p_frame, int p_line, Selection p_sel) const; @@ -549,15 +551,20 @@ public: int get_paragraph_count() const; int get_visible_paragraph_count() const; + float get_line_offset(int p_line); + float get_paragraph_offset(int p_paragraph); + void scroll_to_line(int p_line); int get_line_count() const; int get_visible_line_count() const; int get_content_height() const; + int get_content_width() const; VScrollBar *get_v_scroll_bar() { return vscroll; } virtual CursorShape get_cursor_shape(const Point2 &p_pos) const override; + virtual Variant get_drag_data(const Point2 &p_point) override; void set_selection_enabled(bool p_enabled); bool is_selection_enabled() const; @@ -594,6 +601,8 @@ public: void set_visible_characters(int p_visible); int get_visible_characters() const; + int get_character_line(int p_char); + int get_character_paragraph(int p_char); int get_total_character_count() const; int get_total_glyph_count() const; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 343056957c..e1b0e8cca8 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -218,195 +218,198 @@ void ScrollBar::gui_input(const Ref<InputEvent> &p_event) { } void ScrollBar::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - RID ci = get_canvas_item(); + switch (p_what) { + case NOTIFICATION_DRAW: { + RID ci = get_canvas_item(); - Ref<Texture2D> decr, incr; + Ref<Texture2D> decr, incr; - if (decr_active) { - decr = get_theme_icon(SNAME("decrement_pressed")); - } else if (highlight == HIGHLIGHT_DECR) { - decr = get_theme_icon(SNAME("decrement_highlight")); - } else { - decr = get_theme_icon(SNAME("decrement")); - } + if (decr_active) { + decr = get_theme_icon(SNAME("decrement_pressed")); + } else if (highlight == HIGHLIGHT_DECR) { + decr = get_theme_icon(SNAME("decrement_highlight")); + } else { + decr = get_theme_icon(SNAME("decrement")); + } - if (incr_active) { - incr = get_theme_icon(SNAME("increment_pressed")); - } else if (highlight == HIGHLIGHT_INCR) { - incr = get_theme_icon(SNAME("increment_highlight")); - } else { - incr = get_theme_icon(SNAME("increment")); - } + if (incr_active) { + incr = get_theme_icon(SNAME("increment_pressed")); + } else if (highlight == HIGHLIGHT_INCR) { + incr = get_theme_icon(SNAME("increment_highlight")); + } else { + incr = get_theme_icon(SNAME("increment")); + } - Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll")); + Ref<StyleBox> bg = has_focus() ? get_theme_stylebox(SNAME("scroll_focus")) : get_theme_stylebox(SNAME("scroll")); - Ref<StyleBox> grabber; - if (drag.active) { - grabber = get_theme_stylebox(SNAME("grabber_pressed")); - } else if (highlight == HIGHLIGHT_RANGE) { - grabber = get_theme_stylebox(SNAME("grabber_highlight")); - } else { - grabber = get_theme_stylebox(SNAME("grabber")); - } + Ref<StyleBox> grabber; + if (drag.active) { + grabber = get_theme_stylebox(SNAME("grabber_pressed")); + } else if (highlight == HIGHLIGHT_RANGE) { + grabber = get_theme_stylebox(SNAME("grabber_highlight")); + } else { + grabber = get_theme_stylebox(SNAME("grabber")); + } - Point2 ofs; + Point2 ofs; - decr->draw(ci, Point2()); + decr->draw(ci, Point2()); - if (orientation == HORIZONTAL) { - ofs.x += decr->get_width(); - } else { - ofs.y += decr->get_height(); - } + if (orientation == HORIZONTAL) { + ofs.x += decr->get_width(); + } else { + ofs.y += decr->get_height(); + } - Size2 area = get_size(); + Size2 area = get_size(); - if (orientation == HORIZONTAL) { - area.width -= incr->get_width() + decr->get_width(); - } else { - area.height -= incr->get_height() + decr->get_height(); - } + if (orientation == HORIZONTAL) { + area.width -= incr->get_width() + decr->get_width(); + } else { + area.height -= incr->get_height() + decr->get_height(); + } - bg->draw(ci, Rect2(ofs, area)); + bg->draw(ci, Rect2(ofs, area)); - if (orientation == HORIZONTAL) { - ofs.width += area.width; - } else { - ofs.height += area.height; - } + if (orientation == HORIZONTAL) { + ofs.width += area.width; + } else { + ofs.height += area.height; + } - incr->draw(ci, ofs); - Rect2 grabber_rect; + incr->draw(ci, ofs); + Rect2 grabber_rect; - if (orientation == HORIZONTAL) { - grabber_rect.size.width = get_grabber_size(); - grabber_rect.size.height = get_size().height; - grabber_rect.position.y = 0; - grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT); - } else { - grabber_rect.size.width = get_size().width; - grabber_rect.size.height = get_grabber_size(); - grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP); - grabber_rect.position.x = 0; - } + if (orientation == HORIZONTAL) { + grabber_rect.size.width = get_grabber_size(); + grabber_rect.size.height = get_size().height; + grabber_rect.position.y = 0; + grabber_rect.position.x = get_grabber_offset() + decr->get_width() + bg->get_margin(SIDE_LEFT); + } else { + grabber_rect.size.width = get_size().width; + grabber_rect.size.height = get_grabber_size(); + grabber_rect.position.y = get_grabber_offset() + decr->get_height() + bg->get_margin(SIDE_TOP); + grabber_rect.position.x = 0; + } - grabber->draw(ci, grabber_rect); - } + grabber->draw(ci, grabber_rect); + } break; - if (p_what == NOTIFICATION_ENTER_TREE) { - if (has_node(drag_node_path)) { - Node *n = get_node(drag_node_path); - drag_node = Object::cast_to<Control>(n); - } + case NOTIFICATION_ENTER_TREE: { + if (has_node(drag_node_path)) { + Node *n = get_node(drag_node_path); + drag_node = Object::cast_to<Control>(n); + } - if (drag_node) { - drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); - drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT); - } - } - if (p_what == NOTIFICATION_EXIT_TREE) { - if (drag_node) { - drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); - drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit)); - } + if (drag_node) { + drag_node->connect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); + drag_node->connect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit), varray(), CONNECT_ONESHOT); + } + } break; - drag_node = nullptr; - } + case NOTIFICATION_EXIT_TREE: { + if (drag_node) { + drag_node->disconnect("gui_input", callable_mp(this, &ScrollBar::_drag_node_input)); + drag_node->disconnect("tree_exiting", callable_mp(this, &ScrollBar::_drag_node_exit)); + } - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - if (scrolling) { - if (get_value() != target_scroll) { - double target = target_scroll - get_value(); - double dist = sqrt(target * target); - double vel = ((target / dist) * 500) * get_physics_process_delta_time(); + drag_node = nullptr; + } break; - if (Math::abs(vel) >= dist) { - set_value(target_scroll); + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (scrolling) { + if (get_value() != target_scroll) { + double target = target_scroll - get_value(); + double dist = sqrt(target * target); + double vel = ((target / dist) * 500) * get_physics_process_delta_time(); + + if (Math::abs(vel) >= dist) { + set_value(target_scroll); + scrolling = false; + set_physics_process_internal(false); + } else { + set_value(get_value() + vel); + } + } else { scrolling = false; set_physics_process_internal(false); - } else { - set_value(get_value() + vel); } - } else { - scrolling = false; - set_physics_process_internal(false); - } - } else if (drag_node_touching) { - if (drag_node_touching_deaccel) { - Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0); - pos += drag_node_speed * get_physics_process_delta_time(); + } else if (drag_node_touching) { + if (drag_node_touching_deaccel) { + Vector2 pos = Vector2(orientation == HORIZONTAL ? get_value() : 0, orientation == VERTICAL ? get_value() : 0); + pos += drag_node_speed * get_physics_process_delta_time(); - bool turnoff = false; + bool turnoff = false; - if (orientation == HORIZONTAL) { - if (pos.x < 0) { - pos.x = 0; - turnoff = true; - } + if (orientation == HORIZONTAL) { + if (pos.x < 0) { + pos.x = 0; + turnoff = true; + } - if (pos.x > (get_max() - get_page())) { - pos.x = get_max() - get_page(); - turnoff = true; - } + if (pos.x > (get_max() - get_page())) { + pos.x = get_max() - get_page(); + turnoff = true; + } - set_value(pos.x); + set_value(pos.x); - float sgn_x = drag_node_speed.x < 0 ? -1 : 1; - float val_x = Math::abs(drag_node_speed.x); - val_x -= 1000 * get_physics_process_delta_time(); + float sgn_x = drag_node_speed.x < 0 ? -1 : 1; + float val_x = Math::abs(drag_node_speed.x); + val_x -= 1000 * get_physics_process_delta_time(); - if (val_x < 0) { - turnoff = true; - } + if (val_x < 0) { + turnoff = true; + } - drag_node_speed.x = sgn_x * val_x; + drag_node_speed.x = sgn_x * val_x; - } else { - if (pos.y < 0) { - pos.y = 0; - turnoff = true; - } + } else { + if (pos.y < 0) { + pos.y = 0; + turnoff = true; + } - if (pos.y > (get_max() - get_page())) { - pos.y = get_max() - get_page(); - turnoff = true; - } + if (pos.y > (get_max() - get_page())) { + pos.y = get_max() - get_page(); + turnoff = true; + } - set_value(pos.y); + set_value(pos.y); - float sgn_y = drag_node_speed.y < 0 ? -1 : 1; - float val_y = Math::abs(drag_node_speed.y); - val_y -= 1000 * get_physics_process_delta_time(); + float sgn_y = drag_node_speed.y < 0 ? -1 : 1; + float val_y = Math::abs(drag_node_speed.y); + val_y -= 1000 * get_physics_process_delta_time(); - if (val_y < 0) { - turnoff = true; + if (val_y < 0) { + turnoff = true; + } + drag_node_speed.y = sgn_y * val_y; } - drag_node_speed.y = sgn_y * val_y; - } - if (turnoff) { - set_physics_process_internal(false); - drag_node_touching = false; - drag_node_touching_deaccel = false; - } + if (turnoff) { + set_physics_process_internal(false); + drag_node_touching = false; + drag_node_touching_deaccel = false; + } - } else { - if (time_since_motion == 0 || time_since_motion > 0.1) { - Vector2 diff = drag_node_accum - last_drag_node_accum; - last_drag_node_accum = drag_node_accum; - drag_node_speed = diff / get_physics_process_delta_time(); - } + } else { + if (time_since_motion == 0 || time_since_motion > 0.1) { + Vector2 diff = drag_node_accum - last_drag_node_accum; + last_drag_node_accum = drag_node_accum; + drag_node_speed = diff / get_physics_process_delta_time(); + } - time_since_motion += get_physics_process_delta_time(); + time_since_motion += get_physics_process_delta_time(); + } } - } - } + } break; - if (p_what == NOTIFICATION_MOUSE_EXIT) { - highlight = HIGHLIGHT_NONE; - update(); + case NOTIFICATION_MOUSE_EXIT: { + highlight = HIGHLIGHT_NONE; + update(); + } break; } } @@ -423,11 +426,6 @@ double ScrollBar::get_grabber_size() const { } float page = (get_page() > 0) ? get_page() : 0; - /* - if (grabber_range < get_step()) - grabber_range=get_step(); - */ - double area_size = get_area_size(); double grabber_size = page / range * area_size; return grabber_size + get_grabber_min_size(); diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 5e128d594c..b3cf2cbf7e 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -29,6 +29,8 @@ /*************************************************************************/ #include "scroll_container.h" + +#include "core/config/project_settings.h" #include "core/os/os.h" #include "scene/main/window.h" @@ -314,97 +316,102 @@ void ScrollContainer::_update_dimensions() { } void ScrollContainer::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { - _updating_scrollbars = true; - call_deferred(SNAME("_update_scrollbar_position")); - }; - - if (p_what == NOTIFICATION_READY) { - Viewport *viewport = get_viewport(); - ERR_FAIL_COND(!viewport); - viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed)); - _update_dimensions(); - } - - if (p_what == NOTIFICATION_SORT_CHILDREN) { - _update_dimensions(); - }; - - if (p_what == NOTIFICATION_DRAW) { - Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); - draw_style_box(sb, Rect2(Vector2(), get_size())); - - update_scrollbars(); - } - - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - if (drag_touching) { - if (drag_touching_deaccel) { - Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value()); - pos += drag_speed * get_physics_process_delta_time(); - - bool turnoff_h = false; - bool turnoff_v = false; - - if (pos.x < 0) { - pos.x = 0; - turnoff_h = true; - } - if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) { - pos.x = h_scroll->get_max() - h_scroll->get_page(); - turnoff_h = true; - } - - if (pos.y < 0) { - pos.y = 0; - turnoff_v = true; - } - if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) { - pos.y = v_scroll->get_max() - v_scroll->get_page(); - turnoff_v = true; - } - - if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) { - h_scroll->set_value(pos.x); - } - if (vertical_scroll_mode != SCROLL_MODE_DISABLED) { - v_scroll->set_value(pos.y); - } - - float sgn_x = drag_speed.x < 0 ? -1 : 1; - float val_x = Math::abs(drag_speed.x); - val_x -= 1000 * get_physics_process_delta_time(); - - if (val_x < 0) { - turnoff_h = true; - } - - float sgn_y = drag_speed.y < 0 ? -1 : 1; - float val_y = Math::abs(drag_speed.y); - val_y -= 1000 * get_physics_process_delta_time(); - - if (val_y < 0) { - turnoff_v = true; - } - - drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y); + switch (p_what) { + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + _updating_scrollbars = true; + call_deferred(SNAME("_update_scrollbar_position")); + } break; + + case NOTIFICATION_READY: { + Viewport *viewport = get_viewport(); + ERR_FAIL_COND(!viewport); + viewport->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed)); + _update_dimensions(); + } break; + + case NOTIFICATION_SORT_CHILDREN: { + _update_dimensions(); + } break; + + case NOTIFICATION_DRAW: { + Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg")); + draw_style_box(sb, Rect2(Vector2(), get_size())); + + update_scrollbars(); + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (drag_touching) { + if (drag_touching_deaccel) { + Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value()); + pos += drag_speed * get_physics_process_delta_time(); + + bool turnoff_h = false; + bool turnoff_v = false; + + if (pos.x < 0) { + pos.x = 0; + turnoff_h = true; + } + if (pos.x > (h_scroll->get_max() - h_scroll->get_page())) { + pos.x = h_scroll->get_max() - h_scroll->get_page(); + turnoff_h = true; + } + + if (pos.y < 0) { + pos.y = 0; + turnoff_v = true; + } + if (pos.y > (v_scroll->get_max() - v_scroll->get_page())) { + pos.y = v_scroll->get_max() - v_scroll->get_page(); + turnoff_v = true; + } + + if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) { + h_scroll->set_value(pos.x); + } + if (vertical_scroll_mode != SCROLL_MODE_DISABLED) { + v_scroll->set_value(pos.y); + } + + float sgn_x = drag_speed.x < 0 ? -1 : 1; + float val_x = Math::abs(drag_speed.x); + val_x -= 1000 * get_physics_process_delta_time(); + + if (val_x < 0) { + turnoff_h = true; + } + + float sgn_y = drag_speed.y < 0 ? -1 : 1; + float val_y = Math::abs(drag_speed.y); + val_y -= 1000 * get_physics_process_delta_time(); + + if (val_y < 0) { + turnoff_v = true; + } + + drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y); + + if (turnoff_h && turnoff_v) { + _cancel_drag(); + } - if (turnoff_h && turnoff_v) { - _cancel_drag(); - } + } else { + if (time_since_motion == 0 || time_since_motion > 0.1) { + Vector2 diff = drag_accum - last_drag_accum; + last_drag_accum = drag_accum; + drag_speed = diff / get_physics_process_delta_time(); + } - } else { - if (time_since_motion == 0 || time_since_motion > 0.1) { - Vector2 diff = drag_accum - last_drag_accum; - last_drag_accum = drag_accum; - drag_speed = diff / get_physics_process_delta_time(); + time_since_motion += get_physics_process_delta_time(); } - - time_since_motion += get_physics_process_delta_time(); } - } + } break; } -}; +} void ScrollContainer::update_scrollbars() { Size2 size = get_size(); diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index 9c19eb54dc..e3400d9c8f 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -52,7 +52,6 @@ void Separator::_notification(int p_what) { } else { style->draw(get_canvas_item(), Rect2(0, (size.y - ssize.y) / 2, size.x, ssize.y)); } - } break; } } diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 1d459d589f..4b680f72cf 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "slider.h" + #include "core/os/keyboard.h" Size2 Slider::get_minimum_size() const { @@ -150,19 +151,23 @@ void Slider::_notification(int p_what) { update_minimum_size(); update(); } break; + case NOTIFICATION_MOUSE_ENTER: { mouse_inside = true; update(); } break; + case NOTIFICATION_MOUSE_EXIT: { mouse_inside = false; update(); } break; - case NOTIFICATION_VISIBILITY_CHANGED: // fallthrough + + case NOTIFICATION_VISIBILITY_CHANGED: case NOTIFICATION_EXIT_TREE: { mouse_inside = false; grab.active = false; } break; + case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); Size2i size = get_size(); @@ -209,7 +214,6 @@ void Slider::_notification(int p_what) { } grabber->draw(ci, Point2i(ratio * areasize, size.height / 2 - grabber->get_size().height / 2)); } - } break; } } diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 19d47ea492..5fd31c5416 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -196,34 +196,44 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { } void SpinBox::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - Ref<Texture2D> updown = get_theme_icon(SNAME("updown")); - - _adjust_width_for_icon(updown); - - RID ci = get_canvas_item(); - Size2i size = get_size(); - - if (is_layout_rtl()) { - updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2)); - } else { - updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); - } - - } else if (p_what == NOTIFICATION_FOCUS_EXIT) { - //_value_changed(0); - } else if (p_what == NOTIFICATION_ENTER_TREE) { - _adjust_width_for_icon(get_theme_icon(SNAME("updown"))); - _value_changed(0); - } else if (p_what == NOTIFICATION_EXIT_TREE) { - _release_mouse(); - } else if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { - _value_changed(0); - } else if (p_what == NOTIFICATION_THEME_CHANGED) { - call_deferred(SNAME("update_minimum_size")); - get_line_edit()->call_deferred(SNAME("update_minimum_size")); - } else if (p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { - update(); + switch (p_what) { + case NOTIFICATION_DRAW: { + Ref<Texture2D> updown = get_theme_icon(SNAME("updown")); + + _adjust_width_for_icon(updown); + + RID ci = get_canvas_item(); + Size2i size = get_size(); + + if (is_layout_rtl()) { + updown->draw(ci, Point2i(0, (size.height - updown->get_height()) / 2)); + } else { + updown->draw(ci, Point2i(size.width - updown->get_width(), (size.height - updown->get_height()) / 2)); + } + } break; + + case NOTIFICATION_ENTER_TREE: { + _adjust_width_for_icon(get_theme_icon(SNAME("updown"))); + _value_changed(0); + } break; + + case NOTIFICATION_EXIT_TREE: { + _release_mouse(); + } break; + + case NOTIFICATION_TRANSLATION_CHANGED: { + _value_changed(0); + update(); + } break; + + case NOTIFICATION_THEME_CHANGED: { + call_deferred(SNAME("update_minimum_size")); + get_line_edit()->call_deferred(SNAME("update_minimum_size")); + } break; + + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { + update(); + } break; } } diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 874e5868b6..6845d46721 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -168,15 +168,18 @@ void SplitContainer::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { queue_sort(); } break; + case NOTIFICATION_SORT_CHILDREN: { _resort(); } break; + case NOTIFICATION_MOUSE_EXIT: { mouse_inside = false; if (get_theme_constant(SNAME("autohide"))) { update(); } } break; + case NOTIFICATION_DRAW: { if (!_getch(0) || !_getch(1)) { return; @@ -200,6 +203,7 @@ void SplitContainer::_notification(int p_what) { draw_texture(tex, Point2i(middle_sep + (sep - tex->get_width()) / 2, (size.y - tex->get_height()) / 2)); } } break; + case NOTIFICATION_THEME_CHANGED: { update_minimum_size(); } break; @@ -336,6 +340,30 @@ bool SplitContainer::is_collapsed() const { return collapsed; } +Vector<int> SplitContainer::get_allowed_size_flags_horizontal() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (!vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + +Vector<int> SplitContainer::get_allowed_size_flags_vertical() const { + Vector<int> flags; + flags.append(SIZE_FILL); + if (vertical) { + flags.append(SIZE_EXPAND); + } + flags.append(SIZE_SHRINK_BEGIN); + flags.append(SIZE_SHRINK_CENTER); + flags.append(SIZE_SHRINK_END); + return flags; +} + void SplitContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_split_offset", "offset"), &SplitContainer::set_split_offset); ClassDB::bind_method(D_METHOD("get_split_offset"), &SplitContainer::get_split_offset); diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index ba6fff6f55..a69ffe4de9 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -79,6 +79,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + SplitContainer(bool p_vertical = false); }; diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 760144591e..c66e145bc4 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -91,52 +91,81 @@ int SubViewportContainer::get_stretch_shrink() const { return shrink; } -void SubViewportContainer::_notification(int p_what) { - if (p_what == NOTIFICATION_RESIZED) { - if (!stretch) { - return; - } +Vector<int> SubViewportContainer::get_allowed_size_flags_horizontal() const { + return Vector<int>(); +} + +Vector<int> SubViewportContainer::get_allowed_size_flags_vertical() const { + return Vector<int>(); +} - for (int i = 0; i < get_child_count(); i++) { - SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); - if (!c) { - continue; +void SubViewportContainer::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_RESIZED: { + if (!stretch) { + return; } - c->set_size(get_size() / shrink); - } - } + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); + if (!c) { + continue; + } - if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_VISIBILITY_CHANGED) { - for (int i = 0; i < get_child_count(); i++) { - SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); - if (!c) { - continue; + c->set_size(get_size() / shrink); } - - if (is_visible_in_tree()) { - c->set_update_mode(SubViewport::UPDATE_ALWAYS); - } else { - c->set_update_mode(SubViewport::UPDATE_DISABLED); + } break; + + case NOTIFICATION_ENTER_TREE: + case NOTIFICATION_VISIBILITY_CHANGED: { + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); + if (!c) { + continue; + } + + if (is_visible_in_tree()) { + c->set_update_mode(SubViewport::UPDATE_ALWAYS); + } else { + c->set_update_mode(SubViewport::UPDATE_DISABLED); + } + + c->set_handle_input_locally(false); //do not handle input locally here + } + } break; + + case NOTIFICATION_DRAW: { + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); + if (!c) { + continue; + } + + if (stretch) { + draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size())); + } else { + draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size())); + } } + } break; - c->set_handle_input_locally(false); //do not handle input locally here - } - } + case NOTIFICATION_MOUSE_ENTER: { + _notify_viewports(NOTIFICATION_VP_MOUSE_ENTER); + } break; - if (p_what == NOTIFICATION_DRAW) { - for (int i = 0; i < get_child_count(); i++) { - SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); - if (!c) { - continue; - } + case NOTIFICATION_MOUSE_EXIT: { + _notify_viewports(NOTIFICATION_VP_MOUSE_EXIT); + } break; + } +} - if (stretch) { - draw_texture_rect(c->get_texture(), Rect2(Vector2(), get_size())); - } else { - draw_texture_rect(c->get_texture(), Rect2(Vector2(), c->get_size())); - } +void SubViewportContainer::_notify_viewports(int p_notification) { + for (int i = 0; i < get_child_count(); i++) { + SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); + if (!c) { + continue; } + c->notification(p_notification); } } diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index e7520763fb..f52f01e4e2 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -38,6 +38,7 @@ class SubViewportContainer : public Container { bool stretch = false; int shrink = 1; + void _notify_viewports(int p_notification); protected: void _notification(int p_what); @@ -54,6 +55,9 @@ public: virtual Size2 get_minimum_size() const override; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + SubViewportContainer(); }; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 5a551ec5a5..ce60da762f 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -32,7 +32,6 @@ #include "core/object/message_queue.h" #include "core/string/translation.h" - #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" @@ -313,6 +312,7 @@ void TabBar::_notification(int p_what) { case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: { update(); } break; + case NOTIFICATION_THEME_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < tabs.size(); ++i) { @@ -332,6 +332,7 @@ void TabBar::_notification(int p_what) { ensure_tab_visible(current); } } break; + case NOTIFICATION_DRAW: { if (tabs.is_empty()) { return; @@ -524,13 +525,14 @@ void TabBar::set_tab_count(int p_count) { offset = MIN(offset, p_count - 1); max_drawn_tab = MIN(max_drawn_tab, p_count - 1); current = MIN(current, p_count - 1); - } - _update_cache(); - _ensure_no_over_offset(); - if (scroll_to_selected) { - ensure_tab_visible(current); + _update_cache(); + _ensure_no_over_offset(); + if (scroll_to_selected) { + ensure_tab_visible(current); + } } + update(); update_minimum_size(); notify_property_list_changed(); @@ -761,6 +763,8 @@ void TabBar::_update_hover() { return; } + ERR_FAIL_COND(tabs.is_empty()); + const Point2 &pos = get_local_mouse_position(); // Test hovering to display right or close button. int hover_now = -1; @@ -959,7 +963,6 @@ void TabBar::clear_tabs() { current = 0; previous = 0; - _update_cache(); update(); update_minimum_size(); notify_property_list_changed(); @@ -973,18 +976,21 @@ void TabBar::remove_tab(int p_idx) { } if (current < 0) { + offset = 0; + max_drawn_tab = 0; current = 0; previous = 0; - } - if (current >= tabs.size()) { - current = tabs.size() - 1; - } + } else { + offset = MIN(offset, tabs.size() - 1); + max_drawn_tab = MIN(max_drawn_tab, tabs.size() - 1); - _update_cache(); - _ensure_no_over_offset(); - if (scroll_to_selected && !tabs.is_empty()) { - ensure_tab_visible(current); + _update_cache(); + _ensure_no_over_offset(); + if (scroll_to_selected && !tabs.is_empty()) { + ensure_tab_visible(current); + } } + update(); update_minimum_size(); notify_property_list_changed(); @@ -1436,7 +1442,6 @@ void TabBar::_get_property_list(List<PropertyInfo> *p_list) const { } void TabBar::_bind_methods() { - ClassDB::bind_method(D_METHOD("_update_hover"), &TabBar::_update_hover); ClassDB::bind_method(D_METHOD("set_tab_count", "count"), &TabBar::set_tab_count); ClassDB::bind_method(D_METHOD("get_tab_count"), &TabBar::get_tab_count); ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabBar::set_current_tab); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index c3fc08731e..31a5e41086 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -32,7 +32,6 @@ #include "core/object/message_queue.h" #include "core/string/translation.h" - #include "scene/gui/box_container.h" #include "scene/gui/label.h" #include "scene/gui/texture_rect.h" @@ -326,6 +325,7 @@ void TabContainer::_notification(int p_what) { first_tab_cache--; } } break; + case NOTIFICATION_DRAW: { RID canvas = get_canvas_item(); Size2 size = get_size(); @@ -522,6 +522,7 @@ void TabContainer::_notification(int p_what) { } } } break; + case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { @@ -704,7 +705,7 @@ void TabContainer::add_child_notify(Node *p_child) { } _refresh_texts(); - call_deferred("_repaint"); + call_deferred(SNAME("_repaint")); update(); bool first = (_get_tabs().size() == 1); @@ -1177,6 +1178,14 @@ bool TabContainer::get_use_hidden_tabs_for_min_size() const { return use_hidden_tabs_for_min_size; } +Vector<int> TabContainer::get_allowed_size_flags_horizontal() const { + return Vector<int>(); +} + +Vector<int> TabContainer::get_allowed_size_flags_vertical() const { + return Vector<int>(); +} + void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_count"), &TabContainer::get_tab_count); ClassDB::bind_method(D_METHOD("set_current_tab", "tab_idx"), &TabContainer::set_current_tab); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 01e71e9fa8..ee1b3fea51 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -133,6 +133,9 @@ public: void set_use_hidden_tabs_for_min_size(bool p_use_hidden_tabs); bool get_use_hidden_tabs_for_min_size() const; + virtual Vector<int> get_allowed_size_flags_horizontal() const override; + virtual Vector<int> get_allowed_size_flags_vertical() const override; + TabContainer(); }; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index bb259843b8..5a3c622c86 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -432,32 +432,38 @@ void TextEdit::_notification(int p_what) { } _update_wrap_at_column(true); } break; + case NOTIFICATION_RESIZED: { _update_scrollbars(); _update_wrap_at_column(); } break; + case NOTIFICATION_VISIBILITY_CHANGED: { if (is_visible()) { call_deferred(SNAME("_update_scrollbars")); call_deferred(SNAME("_update_wrap_at_column")); } } break; + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: case NOTIFICATION_THEME_CHANGED: { _update_caches(); _update_wrap_at_column(true); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_IN: { window_has_focus = true; draw_caret = true; update(); } break; + case NOTIFICATION_WM_WINDOW_FOCUS_OUT: { window_has_focus = false; draw_caret = false; update(); } break; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (scrolling && get_v_scroll() != target_v_scroll) { double target_y = target_v_scroll - get_v_scroll(); @@ -479,6 +485,7 @@ void TextEdit::_notification(int p_what) { set_physics_process_internal(false); } } break; + case NOTIFICATION_DRAW: { if (first_draw) { // Size may not be the final one, so attempts to ensure caret was visible may have failed. @@ -961,7 +968,7 @@ void TextEdit::_notification(int p_what) { // Give visual indication of empty selected line. if (selection.active && line >= selection.from_line && line <= selection.to_line && char_margin >= xmargin_beg) { - int char_w = font->get_char_size(' ', 0, font_size).width; + float char_w = font->get_char_size(' ', 0, font_size).width; if (rtl) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(size.width - xmargin_beg - ofs_x - char_w, ofs_y, char_w, row_height), selection_color); } else { @@ -1427,6 +1434,7 @@ void TextEdit::_notification(int p_what) { } } } break; + case NOTIFICATION_FOCUS_ENTER: { if (caret_blink_enabled) { caret_blink_timer->start(); @@ -1458,6 +1466,7 @@ void TextEdit::_notification(int p_what) { DisplayServer::get_singleton()->virtual_keyboard_show(get_text(), get_global_rect(), true, -1, caret_start, caret_end); } } break; + case NOTIFICATION_FOCUS_EXIT: { if (caret_blink_enabled) { caret_blink_timer->stop(); @@ -1481,6 +1490,7 @@ void TextEdit::_notification(int p_what) { deselect(); } } break; + case MainLoop::NOTIFICATION_OS_IME_UPDATE: { if (has_focus()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); @@ -1497,7 +1507,8 @@ void TextEdit::_notification(int p_what) { update(); } } break; - case Control::NOTIFICATION_DRAG_BEGIN: { + + case NOTIFICATION_DRAG_BEGIN: { selection.selecting_mode = SelectionMode::SELECTION_MODE_NONE; drag_action = true; dragging_minimap = false; @@ -1505,7 +1516,8 @@ void TextEdit::_notification(int p_what) { can_drag_minimap = false; click_select_held->stop(); } break; - case Control::NOTIFICATION_DRAG_END: { + + case NOTIFICATION_DRAG_END: { if (is_drag_successful()) { if (selection.drag_attempt) { selection.drag_attempt = false; @@ -2472,7 +2484,7 @@ void TextEdit::_update_placeholder() { return; // Not in tree? } - // Placeholder is generally smaller then text docuemnts, and updates less so this should be fast enough for now. + // Placeholder is generally smaller then text documents, and updates less so this should be fast enough for now. placeholder_data_buf->clear(); placeholder_data_buf->set_width(text.get_width()); placeholder_data_buf->set_direction((TextServer::Direction)text_direction); diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index a8cdeb44f5..ecdf55caf0 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -29,84 +29,87 @@ /*************************************************************************/ #include "texture_rect.h" + #include "core/core_string_names.h" #include "servers/rendering_server.h" void TextureRect::_notification(int p_what) { - if (p_what == NOTIFICATION_DRAW) { - if (texture.is_null()) { - return; - } - - Size2 size; - Point2 offset; - Rect2 region; - bool tile = false; - - switch (stretch_mode) { - case STRETCH_SCALE: { - size = get_size(); - } break; - case STRETCH_TILE: { - size = get_size(); - tile = true; - } break; - case STRETCH_KEEP: { - size = texture->get_size(); - } break; - case STRETCH_KEEP_CENTERED: { - offset = (get_size() - texture->get_size()) / 2; - size = texture->get_size(); - } break; - case STRETCH_KEEP_ASPECT_CENTERED: - case STRETCH_KEEP_ASPECT: { - size = get_size(); - int tex_width = texture->get_width() * size.height / texture->get_height(); - int tex_height = size.height; - - if (tex_width > size.width) { - tex_width = size.width; - tex_height = texture->get_height() * tex_width / texture->get_width(); - } - - if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) { - offset.x += (size.width - tex_width) / 2; - offset.y += (size.height - tex_height) / 2; - } - - size.width = tex_width; - size.height = tex_height; - } break; - case STRETCH_KEEP_ASPECT_COVERED: { - size = get_size(); - - Size2 tex_size = texture->get_size(); - Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); - float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; - Size2 scaled_tex_size = tex_size * scale; - - region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f; - region.size = size / scale; - } break; - } - - Ref<AtlasTexture> p_atlas = texture; - - if (p_atlas.is_valid() && region.has_no_area()) { - Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height()); - - offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0; - offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0; - } - - size.width *= hflip ? -1.0f : 1.0f; - size.height *= vflip ? -1.0f : 1.0f; - - if (region.has_no_area()) { - draw_texture_rect(texture, Rect2(offset, size), tile); - } else { - draw_texture_rect_region(texture, Rect2(offset, size), region); - } + switch (p_what) { + case NOTIFICATION_DRAW: { + if (texture.is_null()) { + return; + } + + Size2 size; + Point2 offset; + Rect2 region; + bool tile = false; + + switch (stretch_mode) { + case STRETCH_SCALE: { + size = get_size(); + } break; + case STRETCH_TILE: { + size = get_size(); + tile = true; + } break; + case STRETCH_KEEP: { + size = texture->get_size(); + } break; + case STRETCH_KEEP_CENTERED: { + offset = (get_size() - texture->get_size()) / 2; + size = texture->get_size(); + } break; + case STRETCH_KEEP_ASPECT_CENTERED: + case STRETCH_KEEP_ASPECT: { + size = get_size(); + int tex_width = texture->get_width() * size.height / texture->get_height(); + int tex_height = size.height; + + if (tex_width > size.width) { + tex_width = size.width; + tex_height = texture->get_height() * tex_width / texture->get_width(); + } + + if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) { + offset.x += (size.width - tex_width) / 2; + offset.y += (size.height - tex_height) / 2; + } + + size.width = tex_width; + size.height = tex_height; + } break; + case STRETCH_KEEP_ASPECT_COVERED: { + size = get_size(); + + Size2 tex_size = texture->get_size(); + Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height); + float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height; + Size2 scaled_tex_size = tex_size * scale; + + region.position = ((scaled_tex_size - size) / scale).abs() / 2.0f; + region.size = size / scale; + } break; + } + + Ref<AtlasTexture> p_atlas = texture; + + if (p_atlas.is_valid() && region.has_no_area()) { + Size2 scale_size(size.width / texture->get_width(), size.height / texture->get_height()); + + offset.width += hflip ? p_atlas->get_margin().get_position().width * scale_size.width * 2 : 0; + offset.height += vflip ? p_atlas->get_margin().get_position().height * scale_size.height * 2 : 0; + } + + size.width *= hflip ? -1.0f : 1.0f; + size.height *= vflip ? -1.0f : 1.0f; + + if (region.has_no_area()) { + draw_texture_rect(texture, Rect2(offset, size), tile); + } else { + draw_texture_rect_region(texture, Rect2(offset, size), region); + } + } break; } } diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index a190e08088..73cf2b9c6e 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -37,10 +37,9 @@ #include "core/os/os.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "scene/gui/box_container.h" #include "scene/main/window.h" -#include "box_container.h" - #include <limits.h> Size2 TreeItem::Cell::get_icon_size() const { @@ -202,7 +201,7 @@ void TreeItem::propagate_check(int p_column, bool p_emit_signal) { bool ch = cells[p_column].checked; if (p_emit_signal) { - tree->emit_signal("check_propagated_to_item", this, p_column); + tree->emit_signal(SNAME("check_propagated_to_item"), this, p_column); } _propagate_check_through_children(p_column, ch, p_emit_signal); _propagate_check_through_parents(p_column, p_emit_signal); @@ -213,7 +212,7 @@ void TreeItem::_propagate_check_through_children(int p_column, bool p_checked, b while (current) { current->set_checked(p_column, p_checked); if (p_emit_signal) { - current->tree->emit_signal("check_propagated_to_item", current, p_column); + current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column); } current->_propagate_check_through_children(p_column, p_checked, p_emit_signal); current = current->get_next(); @@ -252,7 +251,7 @@ void TreeItem::_propagate_check_through_parents(int p_column, bool p_emit_signal } if (p_emit_signal) { - current->tree->emit_signal("check_propagated_to_item", current, p_column); + current->tree->emit_signal(SNAME("check_propagated_to_item"), current, p_column); } current->_propagate_check_through_parents(p_column, p_emit_signal); } @@ -905,6 +904,12 @@ String TreeItem::get_button_tooltip(int p_column, int p_idx) const { return cells[p_column].buttons[p_idx].tooltip; } +int TreeItem::get_button_id(int p_column, int p_idx) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), -1); + ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1); + return cells[p_column].buttons[p_idx].id; +} + void TreeItem::erase_button(int p_column, int p_idx) { ERR_FAIL_INDEX(p_column, cells.size()); ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); @@ -1283,9 +1288,11 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_custom_as_button", "column", "enable"), &TreeItem::set_custom_as_button); ClassDB::bind_method(D_METHOD("is_custom_set_as_button", "column"), &TreeItem::is_custom_set_as_button); - ClassDB::bind_method(D_METHOD("add_button", "column", "button", "button_idx", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count); ClassDB::bind_method(D_METHOD("get_button_tooltip", "column", "button_idx"), &TreeItem::get_button_tooltip); + ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id); + ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id); ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button); ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button); ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button); @@ -1694,8 +1701,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 bool skip = (p_item == root && hide_root); if (!skip && (p_pos.y + label_h - cache.offset.y) > 0) { - //draw separation. - //if (p_item->get_parent()!=root || !hide_root) + // Draw separation. ERR_FAIL_COND_V(cache.font.is_null(), -1); @@ -2252,11 +2258,6 @@ void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_c emit_signal(SNAME("item_selected")); emitted_row = true; } - /* - if (p_col==i) - p_current->selected_signal.call(p_col); - */ - } else if (c.selected) { if (p_selected != p_current) { // Deselect other rows. @@ -3628,178 +3629,187 @@ int Tree::_get_title_button_height() const { } void Tree::_notification(int p_what) { - if (p_what == NOTIFICATION_FOCUS_ENTER) { - if (get_viewport()) { - focus_in_id = get_viewport()->get_processed_events_count(); - } - } - if (p_what == NOTIFICATION_MOUSE_EXIT) { - if (cache.hover_type != Cache::CLICK_NONE) { - cache.hover_type = Cache::CLICK_NONE; - update(); - } - } - - if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { - drag_touching = false; - } + switch (p_what) { + case NOTIFICATION_FOCUS_ENTER: { + if (get_viewport()) { + focus_in_id = get_viewport()->get_processed_events_count(); + } + } break; - if (p_what == NOTIFICATION_ENTER_TREE) { - update_cache(); - } - if (p_what == NOTIFICATION_DRAG_END) { - drop_mode_flags = 0; - scrolling = false; - set_physics_process_internal(false); - update(); - } - if (p_what == NOTIFICATION_DRAG_BEGIN) { - single_select_defer = nullptr; - if (cache.scroll_speed > 0) { - scrolling = true; - set_physics_process_internal(true); - } - } - if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { - if (drag_touching) { - if (drag_touching_deaccel) { - float pos = v_scroll->get_value(); - pos += drag_speed * get_physics_process_delta_time(); + case NOTIFICATION_MOUSE_EXIT: { + if (cache.hover_type != Cache::CLICK_NONE) { + cache.hover_type = Cache::CLICK_NONE; + update(); + } + } break; - bool turnoff = false; - if (pos < 0) { - pos = 0; - turnoff = true; - set_physics_process_internal(false); - drag_touching = false; - drag_touching_deaccel = false; - } - if (pos > (v_scroll->get_max() - v_scroll->get_page())) { - pos = v_scroll->get_max() - v_scroll->get_page(); - turnoff = true; - } + case NOTIFICATION_VISIBILITY_CHANGED: { + drag_touching = false; + } break; - v_scroll->set_value(pos); - float sgn = drag_speed < 0 ? -1 : 1; - float val = Math::abs(drag_speed); - val -= 1000 * get_physics_process_delta_time(); + case NOTIFICATION_ENTER_TREE: { + update_cache(); + } break; - if (val < 0) { - turnoff = true; - } - drag_speed = sgn * val; + case NOTIFICATION_DRAG_END: { + drop_mode_flags = 0; + scrolling = false; + set_physics_process_internal(false); + update(); + } break; - if (turnoff) { - set_physics_process_internal(false); - drag_touching = false; - drag_touching_deaccel = false; - } + case NOTIFICATION_DRAG_BEGIN: { + single_select_defer = nullptr; + if (cache.scroll_speed > 0) { + scrolling = true; + set_physics_process_internal(true); } - } + } break; - Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); - if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { - Point2 point; + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { + if (drag_touching) { + if (drag_touching_deaccel) { + float pos = v_scroll->get_value(); + pos += drag_speed * get_physics_process_delta_time(); - if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) { - point.x = mouse_position.x - cache.scroll_border; - } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) { - point.x = mouse_position.x - (get_size().width - cache.scroll_border); - } + bool turnoff = false; + if (pos < 0) { + pos = 0; + turnoff = true; + set_physics_process_internal(false); + drag_touching = false; + drag_touching_deaccel = false; + } + if (pos > (v_scroll->get_max() - v_scroll->get_page())) { + pos = v_scroll->get_max() - v_scroll->get_page(); + turnoff = true; + } + + v_scroll->set_value(pos); + float sgn = drag_speed < 0 ? -1 : 1; + float val = Math::abs(drag_speed); + val -= 1000 * get_physics_process_delta_time(); - if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) { - point.y = mouse_position.y - cache.scroll_border; - } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) { - point.y = mouse_position.y - (get_size().height - cache.scroll_border); + if (val < 0) { + turnoff = true; + } + drag_speed = sgn * val; + + if (turnoff) { + set_physics_process_internal(false); + drag_touching = false; + drag_touching_deaccel = false; + } + } } - point *= cache.scroll_speed * get_physics_process_delta_time(); - point += get_scroll(); - h_scroll->set_value(point.x); - v_scroll->set_value(point.y); - } - } + Point2 mouse_position = get_viewport()->get_mouse_position() - get_global_position(); + if (scrolling && get_rect().grow(cache.scroll_border).has_point(mouse_position)) { + Point2 point; - if (p_what == NOTIFICATION_DRAW) { - update_cache(); - update_scrollbars(); - RID ci = get_canvas_item(); + if ((ABS(mouse_position.x) < ABS(mouse_position.x - get_size().width)) && (ABS(mouse_position.x) < cache.scroll_border)) { + point.x = mouse_position.x - cache.scroll_border; + } else if (ABS(mouse_position.x - get_size().width) < cache.scroll_border) { + point.x = mouse_position.x - (get_size().width - cache.scroll_border); + } - Ref<StyleBox> bg = cache.bg; - Color font_outline_color = get_theme_color(SNAME("font_outline_color")); - int outline_size = get_theme_constant(SNAME("outline_size")); + if ((ABS(mouse_position.y) < ABS(mouse_position.y - get_size().height)) && (ABS(mouse_position.y) < cache.scroll_border)) { + point.y = mouse_position.y - cache.scroll_border; + } else if (ABS(mouse_position.y - get_size().height) < cache.scroll_border) { + point.y = mouse_position.y - (get_size().height - cache.scroll_border); + } - Point2 draw_ofs; - draw_ofs += bg->get_offset(); - Size2 draw_size = get_size() - bg->get_minimum_size(); - if (h_scroll->is_visible()) { - draw_size.width -= h_scroll->get_minimum_size().width; - } + point *= cache.scroll_speed * get_physics_process_delta_time(); + point += get_scroll(); + h_scroll->set_value(point.x); + v_scroll->set_value(point.y); + } + } break; - bg->draw(ci, Rect2(Point2(), get_size())); + case NOTIFICATION_DRAW: { + update_cache(); + update_scrollbars(); + RID ci = get_canvas_item(); + + Ref<StyleBox> bg = cache.bg; + Color font_outline_color = get_theme_color(SNAME("font_outline_color")); + int outline_size = get_theme_constant(SNAME("outline_size")); + + Point2 draw_ofs; + draw_ofs += bg->get_offset(); + Size2 draw_size = get_size() - bg->get_minimum_size(); + if (h_scroll->is_visible()) { + draw_size.width -= h_scroll->get_minimum_size().width; + } - int tbh = _get_title_button_height(); + bg->draw(ci, Rect2(Point2(), get_size())); - draw_ofs.y += tbh; - draw_size.y -= tbh; + int tbh = _get_title_button_height(); - cache.rtl = is_layout_rtl(); + draw_ofs.y += tbh; + draw_size.y -= tbh; - if (root && get_size().x > 0 && get_size().y > 0) { - draw_item(Point2(), draw_ofs, draw_size, root); - } + cache.rtl = is_layout_rtl(); - if (show_column_titles) { - //title buttons - int ofs2 = cache.bg->get_margin(SIDE_LEFT); - for (int i = 0; i < columns.size(); i++) { - Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); - Ref<Font> f = cache.tb_font; - Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); - if (cache.rtl) { - tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; - } - sb->draw(ci, tbrect); - ofs2 += tbrect.size.width; - //text - int clip_w = tbrect.size.width - sb->get_minimum_size().width; - columns.write[i].text_buf->set_width(clip_w); - - Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2); - if (outline_size > 0 && font_outline_color.a > 0) { - columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); - } - columns[i].text_buf->draw(ci, text_pos, cache.title_button_color); + if (root && get_size().x > 0 && get_size().y > 0) { + draw_item(Point2(), draw_ofs, draw_size, root); } - } - // Draw the background focus outline last, so that it is drawn in front of the section headings. - // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling. - if (has_focus()) { - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); - const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus")); - bg_focus->draw(ci, Rect2(Point2(), get_size())); - RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); - } - } + if (show_column_titles) { + //title buttons + int ofs2 = cache.bg->get_margin(SIDE_LEFT); + for (int i = 0; i < columns.size(); i++) { + Ref<StyleBox> sb = (cache.click_type == Cache::CLICK_TITLE && cache.click_index == i) ? cache.title_button_pressed : ((cache.hover_type == Cache::CLICK_TITLE && cache.hover_index == i) ? cache.title_button_hover : cache.title_button); + Ref<Font> f = cache.tb_font; + Rect2 tbrect = Rect2(ofs2 - cache.offset.x, bg->get_margin(SIDE_TOP), get_column_width(i), tbh); + if (cache.rtl) { + tbrect.position.x = get_size().width - tbrect.size.x - tbrect.position.x; + } + sb->draw(ci, tbrect); + ofs2 += tbrect.size.width; + //text + int clip_w = tbrect.size.width - sb->get_minimum_size().width; + columns.write[i].text_buf->set_width(clip_w); + + Vector2 text_pos = tbrect.position + Point2i(sb->get_offset().x + (tbrect.size.width - columns[i].text_buf->get_size().x) / 2, (tbrect.size.height - columns[i].text_buf->get_size().y) / 2); + if (outline_size > 0 && font_outline_color.a > 0) { + columns[i].text_buf->draw_outline(ci, text_pos, outline_size, font_outline_color); + } + columns[i].text_buf->draw(ci, text_pos, cache.title_button_color); + } + } - if (p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) { - update_cache(); - _update_all(); - } + // Draw the background focus outline last, so that it is drawn in front of the section headings. + // Otherwise, section heading backgrounds can appear to be in front of the focus outline when scrolling. + if (has_focus()) { + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, true); + const Ref<StyleBox> bg_focus = get_theme_stylebox(SNAME("bg_focus")); + bg_focus->draw(ci, Rect2(Point2(), get_size())); + RenderingServer::get_singleton()->canvas_item_add_clip_ignore(ci, false); + } + } break; - if (p_what == NOTIFICATION_RESIZED || p_what == NOTIFICATION_TRANSFORM_CHANGED) { - if (popup_edited_item != nullptr) { - Rect2 rect = popup_edited_item->get_meta("__focus_rect"); - Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2); - Point2i textedpos = get_global_position() + rect.position - ofs; + case NOTIFICATION_THEME_CHANGED: + case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + update_cache(); + _update_all(); + } break; - if (cache.text_editor_position != textedpos) { - cache.text_editor_position = textedpos; - text_editor->set_position(textedpos); - value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height)); + case NOTIFICATION_RESIZED: + case NOTIFICATION_TRANSFORM_CHANGED: { + if (popup_edited_item != nullptr) { + Rect2 rect = popup_edited_item->get_meta("__focus_rect"); + Vector2 ofs(0, (text_editor->get_size().height - rect.size.height) / 2); + Point2i textedpos = get_global_position() + rect.position - ofs; + + if (cache.text_editor_position != textedpos) { + cache.text_editor_position = textedpos; + text_editor->set_position(textedpos); + value_editor->set_position(textedpos + Point2i(0, text_editor->get_size().height)); + } } - } + } break; } } @@ -4049,10 +4059,6 @@ int Tree::get_edited_column() const { } TreeItem *Tree::get_next_selected(TreeItem *p_item) { - /* - if (!p_item) - return nullptr; - */ if (!root) { return nullptr; } @@ -4409,21 +4415,29 @@ Point2 Tree::get_scroll() const { return ofs; } -void Tree::scroll_to_item(TreeItem *p_item) { +void Tree::scroll_to_item(TreeItem *p_item, bool p_center_on_item) { if (!is_visible_in_tree()) { - // hack to work around crash in get_item_rect() if Tree is not in tree. - return; + return; // Hack to work around crash in get_item_rect() if Tree is not in tree. } - // make sure the scrollbar min and max are up to date with latest changes. update_scrollbars(); - const Rect2 r = get_item_rect(p_item); + const real_t tree_height = get_size().y; + const Rect2 item_rect = get_item_rect(p_item); + const real_t item_y = item_rect.position.y; + const real_t item_height = item_rect.size.y + cache.vseparation; - if (r.position.y <= v_scroll->get_value()) { - v_scroll->set_value(r.position.y); - } else if (r.position.y + r.size.y + 2 * cache.vseparation > v_scroll->get_value() + get_size().y) { - v_scroll->set_value(r.position.y + r.size.y + 2 * cache.vseparation - get_size().y); + if (p_center_on_item) { + v_scroll->set_value(item_y - (tree_height - item_height) / 2.0f); + } else { + if (item_y < v_scroll->get_value()) { + v_scroll->set_value(item_y); + } else { + const real_t new_position = item_y + item_height - tree_height; + if (new_position > v_scroll->get_value()) { + v_scroll->set_value(new_position); + } + } } } @@ -4848,6 +4862,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_at_position", "position"), &Tree::get_item_at_position); ClassDB::bind_method(D_METHOD("get_column_at_position", "position"), &Tree::get_column_at_position); ClassDB::bind_method(D_METHOD("get_drop_section_at_position", "position"), &Tree::get_drop_section_at_position); + ClassDB::bind_method(D_METHOD("get_button_id_at_position", "position"), &Tree::get_button_id_at_position); ClassDB::bind_method(D_METHOD("ensure_cursor_is_visible"), &Tree::ensure_cursor_is_visible); @@ -4868,7 +4883,7 @@ void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_column_title_language", "column"), &Tree::get_column_title_language); ClassDB::bind_method(D_METHOD("get_scroll"), &Tree::get_scroll); - ClassDB::bind_method(D_METHOD("scroll_to_item", "item"), &Tree::scroll_to_item); + ClassDB::bind_method(D_METHOD("scroll_to_item", "item", "center_on_item"), &Tree::scroll_to_item, DEFVAL(false)); ClassDB::bind_method(D_METHOD("set_h_scroll_enabled", "h_scroll"), &Tree::set_h_scroll_enabled); ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &Tree::is_h_scroll_enabled); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index c24763a0e4..dc786de6dc 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -248,6 +248,7 @@ public: int get_button_count(int p_column) const; String get_button_tooltip(int p_column, int p_idx) const; Ref<Texture2D> get_button(int p_column, int p_idx) const; + int get_button_id(int p_column, int p_idx) const; void erase_button(int p_column, int p_idx); int get_button_by_id(int p_column, int p_id) const; void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button); @@ -682,7 +683,7 @@ public: TreeItem *get_item_with_text(const String &p_find) const; Point2 get_scroll() const; - void scroll_to_item(TreeItem *p_item); + void scroll_to_item(TreeItem *p_item, bool p_center_on_item = false); void set_h_scroll_enabled(bool p_enable); bool is_h_scroll_enabled() const; void set_v_scroll_enabled(bool p_enable); diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 1f2a8c8aa1..d7c76aa070 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -134,7 +134,6 @@ void VideoStreamPlayer::_notification(int p_notification) { if (stream.is_valid() && autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } - } break; case NOTIFICATION_EXIT_TREE: { @@ -162,7 +161,6 @@ void VideoStreamPlayer::_notification(int p_notification) { if (!playback->is_playing()) { emit_signal(SceneStringNames::get_singleton()->finished); } - } break; case NOTIFICATION_DRAW: { @@ -175,10 +173,9 @@ void VideoStreamPlayer::_notification(int p_notification) { Size2 s = expand ? get_size() : texture->get_size(); draw_texture_rect(texture, Rect2(Point2(), s), false); - } break; - }; -}; + } +} Size2 VideoStreamPlayer::get_minimum_size() const { if (!expand && !texture.is_null()) { @@ -243,11 +240,11 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { if (!expand) { update_minimum_size(); } -}; +} Ref<VideoStream> VideoStreamPlayer::get_stream() const { return stream; -}; +} void VideoStreamPlayer::play() { ERR_FAIL_COND(!is_inside_tree()); @@ -257,10 +254,8 @@ void VideoStreamPlayer::play() { playback->stop(); playback->play(); set_process_internal(true); - // AudioServer::get_singleton()->stream_set_active(stream_rid,true); - // AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume); last_audio_time = 0; -}; +} void VideoStreamPlayer::stop() { if (!is_inside_tree()) { @@ -271,11 +266,10 @@ void VideoStreamPlayer::stop() { } playback->stop(); - // AudioServer::get_singleton()->stream_set_active(stream_rid,false); resampler.flush(); set_process_internal(false); last_audio_time = 0; -}; +} bool VideoStreamPlayer::is_playing() const { if (playback.is_null()) { @@ -283,16 +277,16 @@ bool VideoStreamPlayer::is_playing() const { } return playback->is_playing(); -}; +} void VideoStreamPlayer::set_paused(bool p_paused) { paused = p_paused; if (playback.is_valid()) { playback->set_paused(p_paused); set_process_internal(!p_paused); - }; + } last_audio_time = 0; -}; +} bool VideoStreamPlayer::is_paused() const { return paused; @@ -316,11 +310,11 @@ int VideoStreamPlayer::get_audio_track() const { void VideoStreamPlayer::set_volume(float p_vol) { volume = p_vol; -}; +} float VideoStreamPlayer::get_volume() const { return volume; -}; +} void VideoStreamPlayer::set_volume_db(float p_db) { if (p_db < -79) { @@ -328,7 +322,7 @@ void VideoStreamPlayer::set_volume_db(float p_db) { } else { set_volume(Math::db2linear(p_db)); } -}; +} float VideoStreamPlayer::get_volume_db() const { if (volume == 0) { @@ -336,21 +330,21 @@ float VideoStreamPlayer::get_volume_db() const { } else { return Math::linear2db(volume); } -}; +} String VideoStreamPlayer::get_stream_name() const { if (stream.is_null()) { return "<No Stream>"; } return stream->get_name(); -}; +} float VideoStreamPlayer::get_stream_position() const { if (playback.is_null()) { return 0; } return playback->get_playback_position(); -}; +} void VideoStreamPlayer::set_stream_position(float p_position) { if (playback.is_valid()) { @@ -368,14 +362,14 @@ Ref<Texture2D> VideoStreamPlayer::get_video_texture() const { void VideoStreamPlayer::set_autoplay(bool p_enable) { autoplay = p_enable; -}; +} bool VideoStreamPlayer::has_autoplay() const { return autoplay; -}; +} void VideoStreamPlayer::set_bus(const StringName &p_bus) { - //if audio is active, must lock this + // If audio is active, must lock this. AudioServer::get_singleton()->lock(); bus = p_bus; AudioServer::get_singleton()->unlock(); @@ -449,7 +443,6 @@ void VideoStreamPlayer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "audio_track", PROPERTY_HINT_RANGE, "0,128,1"), "set_audio_track", "get_audio_track"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "VideoStream"), "set_stream", "get_stream"); - //ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), "set_loop", "has_loop") ; ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_db", PROPERTY_HINT_RANGE, "-80,24,0.01"), "set_volume_db", "get_volume_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume", PROPERTY_HINT_RANGE, "0,15,0.01,exp", PROPERTY_USAGE_NONE), "set_volume", "get_volume"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "has_autoplay"); @@ -464,7 +457,5 @@ void VideoStreamPlayer::_bind_methods() { VideoStreamPlayer::VideoStreamPlayer() {} VideoStreamPlayer::~VideoStreamPlayer() { - // if (stream_rid.is_valid()) - // AudioServer::get_singleton()->free(stream_rid); - resampler.clear(); //Not necessary here, but make in consistent with other "stream_player" classes -}; + resampler.clear(); // Not necessary here, but make in consistent with other "stream_player" classes. +} |