diff options
Diffstat (limited to 'scene/gui')
44 files changed, 1397 insertions, 612 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index f57afb66b3..66b14dc967 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -380,7 +380,7 @@ void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) { queue_redraw(); accept_event(); - if (shortcut_feedback) { + if (shortcut_feedback && is_inside_tree()) { if (shortcut_feedback_timer == nullptr) { shortcut_feedback_timer = memnew(Timer); shortcut_feedback_timer->set_one_shot(true); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index e7a2a26a29..291d578add 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -479,19 +479,8 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } - if (k->is_action("ui_home", true)) { - code_completion_current_selected = 0; - code_completion_force_item_center = -1; - queue_redraw(); - accept_event(); - return; - } - if (k->is_action("ui_end", true)) { - code_completion_current_selected = code_completion_options.size() - 1; - code_completion_force_item_center = -1; - queue_redraw(); - accept_event(); - return; + if (k->is_action("ui_text_caret_line_start", true) || k->is_action("ui_text_caret_line_end", true)) { + cancel_code_completion(); } if (k->is_action("ui_text_completion_replace", true) || k->is_action("ui_text_completion_accept", true)) { confirm_code_completion(k->is_action("ui_text_completion_replace", true)); @@ -2873,7 +2862,8 @@ void CodeEdit::_update_code_region_tags() { return; } - for (int i = 0; i < delimiters.size(); i++) { + // A shorter delimiter has higher priority. + for (int i = delimiters.size() - 1; i >= 0; i--) { if (delimiters[i].type != DelimiterType::TYPE_COMMENT) { continue; } @@ -3115,6 +3105,8 @@ void CodeEdit::_add_delimiter(const String &p_start_key, const String &p_end_key ERR_FAIL_COND_MSG(delimiters[i].start_key == p_start_key, "delimiter with start key '" + p_start_key + "' already exists."); if (p_start_key.length() < delimiters[i].start_key.length()) { at++; + } else { + break; } } @@ -3223,7 +3215,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { int line_height = get_line_height(); if (GDVIRTUAL_IS_OVERRIDDEN(_filter_code_completion_candidates)) { - code_completion_options.clear(); + Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new; code_completion_base = ""; /* Build options argument. */ @@ -3273,11 +3265,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); + } + + if (_should_reset_selected_option_for_new_options(code_completion_options_new)) { + code_completion_current_selected = 0; } + code_completion_options = code_completion_options_new; code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); - code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); @@ -3352,7 +3348,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { /* For now handle only traditional quoted strings. */ bool single_quote = in_string != -1 && first_quote_col > 0 && delimiters[in_string].start_key == "'"; - code_completion_options.clear(); + Vector<ScriptLanguage::CodeCompletionOption> code_completion_options_new; code_completion_base = string_to_complete; /* Don't autocomplete setting numerical values. */ @@ -3384,7 +3380,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (string_to_complete.length() == 0) { option.get_option_characteristics(string_to_complete); - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); + if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } @@ -3459,7 +3456,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } } - code_completion_options.push_back(option); + code_completion_options_new.push_back(option); if (theme_cache.font.is_valid()) { max_width = MAX(max_width, theme_cache.font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, theme_cache.font_size).width + offset); } @@ -3467,26 +3464,46 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } /* No options to complete, cancel. */ - if (code_completion_options.size() == 0) { + if (code_completion_options_new.size() == 0) { cancel_code_completion(); return; } /* A perfect match, stop completion. */ - if (code_completion_options.size() == 1 && string_to_complete == code_completion_options[0].display) { + if (code_completion_options_new.size() == 1 && string_to_complete == code_completion_options_new[0].display) { cancel_code_completion(); return; } - code_completion_options.sort_custom<CodeCompletionOptionCompare>(); + code_completion_options_new.sort_custom<CodeCompletionOptionCompare>(); + if (_should_reset_selected_option_for_new_options(code_completion_options_new)) { + code_completion_current_selected = 0; + } + code_completion_options = code_completion_options_new; code_completion_longest_line = MIN(max_width, theme_cache.code_completion_max_width * theme_cache.font_size); - code_completion_current_selected = 0; code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); } +// Assumes both the new_options and the code_completion_options are sorted. +bool CodeEdit::_should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options) { + if (code_completion_current_selected >= p_new_options.size()) { + return true; + } + + for (int i = 0; i < code_completion_options.size() && i < p_new_options.size(); i++) { + if (i > code_completion_current_selected) { + return false; + } + if (code_completion_options[i].display != p_new_options[i].display) { + return true; + } + } + return false; +} + void CodeEdit::_lines_edited_from(int p_from_line, int p_to_line) { _update_delimiter_cache(p_from_line, p_to_line); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 97c435b52d..4b0629d29d 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -222,6 +222,7 @@ private: void _update_scroll_selected_line(float p_mouse_y); void _filter_code_completion_candidates_impl(); + bool _should_reset_selected_option_for_new_options(const Vector<ScriptLanguage::CodeCompletionOption> &p_new_options); /* Line length guidelines */ TypedArray<int> line_length_guideline_columns; diff --git a/scene/gui/color_mode.h b/scene/gui/color_mode.h index bbbf3fbaac..84e9d4542d 100644 --- a/scene/gui/color_mode.h +++ b/scene/gui/color_mode.h @@ -43,6 +43,7 @@ public: virtual int get_slider_count() const { return 3; }; virtual float get_slider_step() const = 0; + virtual float get_spinbox_arrow_step() const { return get_slider_step(); }; virtual String get_slider_label(int idx) const = 0; virtual float get_slider_max(int idx) const = 0; virtual float get_slider_value(int idx) const = 0; @@ -109,7 +110,8 @@ public: virtual String get_name() const override { return "RAW"; } - virtual float get_slider_step() const override { return 0.01; } + virtual float get_slider_step() const override { return 0.001; } + virtual float get_spinbox_arrow_step() const override { return 0.01; } virtual String get_slider_label(int idx) const override; virtual float get_slider_max(int idx) const override; virtual float get_slider_value(int idx) const override; diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 5ec0714b64..f250662be0 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -77,9 +77,11 @@ void ColorPicker::_notification(int p_what) { alpha_label->add_theme_constant_override(SNAME("center_grabber"), theme_cache.center_slider_grabbers); for (int i = 0; i < MODE_BUTTON_COUNT; i++) { + mode_btns[i]->begin_bulk_theme_override(); mode_btns[i]->add_theme_style_override(SNAME("pressed"), theme_cache.mode_button_pressed); mode_btns[i]->add_theme_style_override(SNAME("normal"), theme_cache.mode_button_normal); mode_btns[i]->add_theme_style_override(SNAME("hover"), theme_cache.mode_button_hover); + mode_btns[i]->end_bulk_theme_override(); } shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_HSV_RECTANGLE), theme_cache.shape_rect); @@ -87,10 +89,16 @@ void ColorPicker::_notification(int p_what) { shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_VHS_CIRCLE), theme_cache.shape_circle); shape_popup->set_item_icon(shape_popup->get_item_index(SHAPE_OKHSL_CIRCLE), theme_cache.shape_circle); + if (current_shape != SHAPE_NONE) { + btn_shape->set_icon(shape_popup->get_item_icon(current_shape)); + } + + internal_margin->begin_bulk_theme_override(); internal_margin->add_theme_constant_override(SNAME("margin_bottom"), theme_cache.content_margin); internal_margin->add_theme_constant_override(SNAME("margin_left"), theme_cache.content_margin); internal_margin->add_theme_constant_override(SNAME("margin_right"), theme_cache.content_margin); internal_margin->add_theme_constant_override(SNAME("margin_top"), theme_cache.content_margin); + internal_margin->end_bulk_theme_override(); _reset_sliders_theme(); @@ -341,7 +349,11 @@ bool ColorPicker::is_editing_alpha() const { return edit_alpha; } -void ColorPicker::_value_changed(double) { +void ColorPicker::_slider_drag_started() { + currently_dragging = true; +} + +void ColorPicker::_slider_value_changed() { if (updating) { return; } @@ -357,7 +369,16 @@ void ColorPicker::_value_changed(double) { } _set_pick_color(color, false); - emit_signal(SNAME("color_changed"), color); + if (!deferred_mode_enabled || !currently_dragging) { + emit_signal(SNAME("color_changed"), color); + } +} + +void ColorPicker::_slider_drag_ended() { + currently_dragging = false; + if (deferred_mode_enabled) { + emit_signal(SNAME("color_changed"), color); + } } void ColorPicker::add_mode(ColorMode *p_mode) { @@ -388,7 +409,9 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) { slider->set_h_size_flags(SIZE_EXPAND_FILL); - slider->connect("value_changed", callable_mp(this, &ColorPicker::_value_changed)); + slider->connect("drag_started", callable_mp(this, &ColorPicker::_slider_drag_started)); + slider->connect("value_changed", callable_mp(this, &ColorPicker::_slider_value_changed).unbind(1)); + slider->connect("drag_ended", callable_mp(this, &ColorPicker::_slider_drag_ended).unbind(1)); slider->connect("draw", callable_mp(this, &ColorPicker::_slider_draw).bind(idx)); slider->connect("gui_input", callable_mp(this, &ColorPicker::_slider_or_spin_input)); @@ -506,20 +529,26 @@ void ColorPicker::_reset_sliders_theme() { Ref<StyleBoxFlat> style_box_flat(memnew(StyleBoxFlat)); style_box_flat->set_content_margin(SIDE_TOP, 16 * theme_cache.base_scale); style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp()); + for (int i = 0; i < SLIDER_COUNT; i++) { + sliders[i]->begin_bulk_theme_override(); sliders[i]->add_theme_icon_override("grabber", theme_cache.bar_arrow); sliders[i]->add_theme_icon_override("grabber_highlight", theme_cache.bar_arrow); sliders[i]->add_theme_constant_override("grabber_offset", 8 * theme_cache.base_scale); if (!colorize_sliders) { sliders[i]->add_theme_style_override("slider", style_box_flat); } + sliders[i]->end_bulk_theme_override(); } + + alpha_slider->begin_bulk_theme_override(); alpha_slider->add_theme_icon_override("grabber", theme_cache.bar_arrow); alpha_slider->add_theme_icon_override("grabber_highlight", theme_cache.bar_arrow); alpha_slider->add_theme_constant_override("grabber_offset", 8 * theme_cache.base_scale); if (!colorize_sliders) { alpha_slider->add_theme_style_override("slider", style_box_flat); } + alpha_slider->end_bulk_theme_override(); } void ColorPicker::_html_submitted(const String &p_html) { @@ -550,9 +579,11 @@ void ColorPicker::_update_color(bool p_update_sliders) { if (p_update_sliders) { float step = modes[current_mode]->get_slider_step(); + float spinbox_arrow_step = modes[current_mode]->get_spinbox_arrow_step(); for (int i = 0; i < current_slider_count; i++) { sliders[i]->set_max(modes[current_mode]->get_slider_max(i)); sliders[i]->set_step(step); + values[i]->set_custom_arrow_step(spinbox_arrow_step); sliders[i]->set_value(modes[current_mode]->get_slider_value(i)); } alpha_slider->set_max(modes[current_mode]->get_slider_max(current_slider_count)); @@ -1242,7 +1273,6 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { _copy_hsv_to_color(); last_color = color; set_pick_color(color); - _update_color(); if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); @@ -1293,7 +1323,6 @@ void ColorPicker::_uv_input(const Ref<InputEvent> &p_event, Control *c) { _copy_hsv_to_color(); last_color = color; set_pick_color(color); - _update_color(); if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); @@ -1321,7 +1350,6 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { _copy_hsv_to_color(); last_color = color; set_pick_color(color); - _update_color(); if (!bev->is_pressed() && bev->get_button_index() == MouseButton::LEFT) { add_recent_preset(color); @@ -1347,7 +1375,6 @@ void ColorPicker::_w_input(const Ref<InputEvent> &p_event) { _copy_hsv_to_color(); last_color = color; set_pick_color(color); - _update_color(); if (!deferred_mode_enabled) { emit_signal(SNAME("color_changed"), color); @@ -1769,8 +1796,6 @@ ColorPicker::ColorPicker() { shape_popup->set_item_checked(current_shape, true); shape_popup->connect("id_pressed", callable_mp(this, &ColorPicker::set_picker_shape)); - btn_shape->set_icon(shape_popup->get_item_icon(current_shape)); - add_mode(new ColorModeRGB(this)); add_mode(new ColorModeHSV(this)); add_mode(new ColorModeRAW(this)); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 96dbca9a0c..dc547c8b0c 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -207,6 +207,7 @@ private: bool hex_visible = true; bool line_edit_mouse_release = false; bool text_changed = false; + bool currently_dragging = false; float h = 0.0; float s = 0.0; @@ -254,7 +255,9 @@ private: void create_slider(GridContainer *gc, int idx); void _reset_sliders_theme(); void _html_submitted(const String &p_html); - void _value_changed(double); + void _slider_drag_started(); + void _slider_value_changed(); + void _slider_drag_ended(); void _update_controls(); void _update_color(bool p_update_sliders = true); void _update_text_value(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 53e8945933..ed54bd000c 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -1653,6 +1653,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) { data.custom_minimum_size = p_custom; update_minimum_size(); + update_configuration_warnings(); } Size2 Control::get_custom_minimum_size() const { @@ -1831,9 +1832,18 @@ bool Control::has_point(const Point2 &p_point) const { void Control::set_mouse_filter(MouseFilter p_filter) { ERR_MAIN_THREAD_GUARD; ERR_FAIL_INDEX(p_filter, 3); + + if (data.mouse_filter == p_filter) { + return; + } + data.mouse_filter = p_filter; notify_property_list_changed(); update_configuration_warnings(); + + if (get_viewport()) { + get_viewport()->_gui_update_mouse_over(); + } } Control::MouseFilter Control::get_mouse_filter() const { @@ -2533,7 +2543,7 @@ StringName Control::get_theme_type_variation() const { Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(Ref<Texture2D>()); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2557,7 +2567,7 @@ Ref<Texture2D> Control::get_theme_icon(const StringName &p_name, const StringNam Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(Ref<StyleBox>()); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2581,7 +2591,7 @@ Ref<StyleBox> Control::get_theme_stylebox(const StringName &p_name, const String Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(Ref<Font>()); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2605,7 +2615,7 @@ Ref<Font> Control::get_theme_font(const StringName &p_name, const StringName &p_ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2629,7 +2639,7 @@ int Control::get_theme_font_size(const StringName &p_name, const StringName &p_t Color Control::get_theme_color(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(Color()); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2653,7 +2663,7 @@ Color Control::get_theme_color(const StringName &p_name, const StringName &p_the int Control::get_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(0); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2704,7 +2714,7 @@ Ref<Texture2D> Control::get_editor_theme_icon(const StringName &p_name) const { bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2721,7 +2731,7 @@ bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2738,7 +2748,7 @@ bool Control::has_theme_stylebox(const StringName &p_name, const StringName &p_t bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2755,7 +2765,7 @@ bool Control::has_theme_font(const StringName &p_name, const StringName &p_theme bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2772,7 +2782,7 @@ bool Control::has_theme_font_size(const StringName &p_name, const StringName &p_ bool Control::has_theme_color(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -2789,7 +2799,7 @@ bool Control::has_theme_color(const StringName &p_name, const StringName &p_them bool Control::has_theme_constant(const StringName &p_name, const StringName &p_theme_type) const { ERR_READ_THREAD_GUARD_V(false); if (!data.initialized) { - WARN_PRINT_ONCE("Attempting to access theme items too early; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED"); + WARN_PRINT_ONCE(vformat("Attempting to access theme items too early in %s; prefer NOTIFICATION_POSTINITIALIZE and NOTIFICATION_THEME_CHANGED", this->get_description())); } if (p_theme_type == StringName() || p_theme_type == get_class_name() || p_theme_type == data.theme_type_variation) { @@ -3568,6 +3578,8 @@ void Control::_bind_methods() { BIND_CONSTANT(NOTIFICATION_RESIZED); BIND_CONSTANT(NOTIFICATION_MOUSE_ENTER); BIND_CONSTANT(NOTIFICATION_MOUSE_EXIT); + BIND_CONSTANT(NOTIFICATION_MOUSE_ENTER_SELF); + BIND_CONSTANT(NOTIFICATION_MOUSE_EXIT_SELF); BIND_CONSTANT(NOTIFICATION_FOCUS_ENTER); BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT); BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); diff --git a/scene/gui/control.h b/scene/gui/control.h index abbdc42fa4..db1bd3a346 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -368,6 +368,8 @@ public: NOTIFICATION_SCROLL_BEGIN = 47, NOTIFICATION_SCROLL_END = 48, NOTIFICATION_LAYOUT_DIRECTION_CHANGED = 49, + NOTIFICATION_MOUSE_ENTER_SELF = 60, + NOTIFICATION_MOUSE_EXIT_SELF = 61, }; // Editor plugin interoperability. diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index aff0ed6f06..957a8f276e 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -405,6 +405,7 @@ AcceptDialog::AcceptDialog() { set_transient(true); set_exclusive(true); set_clamp_to_embedder(true); + set_keep_title_visible(true); bg_panel = memnew(Panel); add_child(bg_panel, false, INTERNAL_MODE_FRONT); diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index 3857281a66..d721ee3ec3 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -56,6 +56,12 @@ void FileDialog::_focus_file_text() { } void FileDialog::popup(const Rect2i &p_rect) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + ConfirmationDialog::popup(p_rect); + } +#endif + if (access == ACCESS_FILESYSTEM && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { DisplayServer::get_singleton()->file_dialog_show(get_title(), dir->get_text(), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); } else { @@ -64,6 +70,13 @@ void FileDialog::popup(const Rect2i &p_rect) { } void FileDialog::set_visible(bool p_visible) { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + ConfirmationDialog::set_visible(p_visible); + return; + } +#endif + if (access == ACCESS_FILESYSTEM && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_NATIVE_DIALOG) && (use_native_dialog || OS::get_singleton()->is_sandboxed())) { if (p_visible) { DisplayServer::get_singleton()->file_dialog_show(get_title(), dir->get_text(), file->get_text().get_file(), show_hidden_files, DisplayServer::FileDialogMode(mode), filters, callable_mp(this, &FileDialog::_native_dialog_cb)); @@ -73,7 +86,7 @@ void FileDialog::set_visible(bool p_visible) { } } -void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { +void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter) { if (p_ok) { if (p_files.size() > 0) { String f = p_files[0]; @@ -90,6 +103,7 @@ void FileDialog::_native_dialog_cb(bool p_ok, const Vector<String> &p_files) { } file->set_text(f); dir->set_text(f.get_base_dir()); + _filter_selected(p_filter); } } else { file->set_text(""); @@ -130,30 +144,40 @@ void FileDialog::_notification(int p_what) { refresh->set_icon(theme_cache.reload); show_hidden->set_icon(theme_cache.toggle_hidden); + dir_up->begin_bulk_theme_override(); dir_up->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); dir_up->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); dir_up->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); dir_up->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + dir_up->end_bulk_theme_override(); + dir_prev->begin_bulk_theme_override(); dir_prev->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); dir_prev->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); dir_prev->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); dir_prev->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + dir_prev->end_bulk_theme_override(); + dir_next->begin_bulk_theme_override(); dir_next->add_theme_color_override("icon_color_normal", theme_cache.icon_normal_color); dir_next->add_theme_color_override("icon_color_hover", theme_cache.icon_hover_color); dir_next->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); dir_next->add_theme_color_override("icon_color_pressed", theme_cache.icon_pressed_color); + dir_next->end_bulk_theme_override(); + refresh->begin_bulk_theme_override(); refresh->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); refresh->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); refresh->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); refresh->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + refresh->end_bulk_theme_override(); + show_hidden->begin_bulk_theme_override(); show_hidden->add_theme_color_override("icon_normal_color", theme_cache.icon_normal_color); show_hidden->add_theme_color_override("icon_hover_color", theme_cache.icon_hover_color); show_hidden->add_theme_color_override("icon_focus_color", theme_cache.icon_focus_color); show_hidden->add_theme_color_override("icon_pressed_color", theme_cache.icon_pressed_color); + show_hidden->end_bulk_theme_override(); invalidate(); } break; @@ -662,7 +686,7 @@ void FileDialog::update_file_list() { files.pop_front(); } - if (mode != FILE_MODE_SAVE_FILE) { + if (mode != FILE_MODE_SAVE_FILE && mode != FILE_MODE_OPEN_DIR) { // Select the first file from list if nothing is selected. if (tree->get_root() && tree->get_root()->get_first_child() && tree->get_selected() == nullptr) { tree->get_root()->get_first_child()->select(0); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index 1a87b79fdd..8ae84fc9dc 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -159,7 +159,7 @@ private: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; - void _native_dialog_cb(bool p_ok, const Vector<String> &p_files); + void _native_dialog_cb(bool p_ok, const Vector<String> &p_files, int p_filter); bool _is_open_should_be_disabled(); diff --git a/scene/gui/graph_edit.compat.inc b/scene/gui/graph_edit.compat.inc new file mode 100644 index 0000000000..9059637a2a --- /dev/null +++ b/scene/gui/graph_edit.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* graph_edit.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +bool GraphEdit::_is_arrange_nodes_button_hidden_bind_compat_81582() const { + return !is_showing_arrange_button(); +} + +void GraphEdit::_set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable) { + set_show_arrange_button(!p_enable); +} + +void GraphEdit::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("is_arrange_nodes_button_hidden"), &GraphEdit::_is_arrange_nodes_button_hidden_bind_compat_81582); + ClassDB::bind_compatibility_method(D_METHOD("set_arrange_nodes_button_hidden", "enable"), &GraphEdit::_set_arrange_nodes_button_hidden_bind_compat_81582); +} + +#endif diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 6e12e7f196..8dddbf78cf 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "graph_edit.h" +#include "graph_edit.compat.inc" #include "core/input/input.h" #include "core/math/math_funcs.h" @@ -36,6 +37,10 @@ #include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/graph_edit_arranger.h" +#include "scene/gui/label.h" +#include "scene/gui/panel_container.h" +#include "scene/gui/scroll_bar.h" +#include "scene/gui/spin_box.h" #include "scene/gui/view_panner.h" #include "scene/resources/style_box_flat.h" #include "scene/theme/theme_db.h" @@ -507,11 +512,13 @@ void GraphEdit::_notification(int p_what) { zoom_plus_button->set_icon(theme_cache.zoom_in); toggle_snapping_button->set_icon(theme_cache.snapping_toggle); - show_grid_button->set_icon(theme_cache.grid_toggle); + toggle_grid_button->set_icon(theme_cache.grid_toggle); minimap_button->set_icon(theme_cache.minimap_toggle); - layout_button->set_icon(theme_cache.layout); + arrange_button->set_icon(theme_cache.layout); zoom_label->set_custom_minimum_size(Size2(48, 0) * theme_cache.base_scale); + + menu_panel->add_theme_style_override("panel", theme_cache.menu_panel); } break; case NOTIFICATION_READY: { @@ -591,7 +598,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { // Determine slot height. int slot_index = graph_node->get_input_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); @@ -605,7 +612,7 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { // Determine slot height. int slot_index = graph_node->get_output_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); if (is_in_output_hotzone(graph_node, j, p_point / zoom, port_size)) { @@ -636,7 +643,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { // Determine slot height. int slot_index = graph_node->get_output_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); if (is_in_output_hotzone(graph_node, j, click_pos, port_size)) { @@ -693,7 +700,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { // Determine slot height. int slot_index = graph_node->get_input_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); if (is_in_input_hotzone(graph_node, j, click_pos, port_size)) { @@ -770,7 +777,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { // Determine slot height. int slot_index = graph_node->get_output_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); int type = graph_node->get_output_port_type(j); @@ -794,7 +801,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { // Determine slot height. int slot_index = graph_node->get_input_port_slot(j); - Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index)); + Control *child = Object::cast_to<Control>(graph_node->get_child(slot_index, false)); port_size.height = MAX(port_size.height, child ? child->get_size().y : 0); int type = graph_node->get_input_port_type(j); @@ -1384,7 +1391,7 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { } } - emit_signal(SNAME("close_nodes_request"), nodes); + emit_signal(SNAME("delete_nodes_request"), nodes); accept_event(); } } @@ -1402,7 +1409,7 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) { for (Connection &E : connections) { if (E.from_node == p_from && E.from_port == p_from_port && E.to_node == p_to && E.to_port == p_to_port) { - if (Math::is_equal_approx(E.activity, p_activity)) { + if (!Math::is_equal_approx(E.activity, p_activity)) { // Update only if changed. top_layer->queue_redraw(); minimap->queue_redraw(); @@ -1528,18 +1535,6 @@ float GraphEdit::get_zoom_max() const { return zoom_max; } -void GraphEdit::set_show_zoom_label(bool p_enable) { - if (zoom_label->is_visible() == p_enable) { - return; - } - - zoom_label->set_visible(p_enable); -} - -bool GraphEdit::is_showing_zoom_label() const { - return zoom_label->is_visible(); -} - void GraphEdit::set_right_disconnects(bool p_enable) { right_disconnects = p_enable; } @@ -1644,7 +1639,7 @@ void GraphEdit::set_show_grid(bool p_show) { } show_grid = p_show; - show_grid_button->set_pressed(p_show); + toggle_grid_button->set_pressed(p_show); queue_redraw(); } @@ -1662,7 +1657,7 @@ void GraphEdit::_snapping_distance_changed(double) { } void GraphEdit::_show_grid_toggled() { - show_grid = show_grid_button->is_pressed(); + show_grid = toggle_grid_button->is_pressed(); queue_redraw(); } @@ -1708,17 +1703,64 @@ bool GraphEdit::is_minimap_enabled() const { return minimap_button->is_pressed(); } -void GraphEdit::set_arrange_nodes_button_hidden(bool p_enable) { - arrange_nodes_button_hidden = p_enable; - if (arrange_nodes_button_hidden) { - layout_button->hide(); - } else { - layout_button->show(); - } +void GraphEdit::set_show_menu(bool p_hidden) { + show_menu = p_hidden; + menu_panel->set_visible(show_menu); +} + +bool GraphEdit::is_showing_menu() const { + return show_menu; +} + +void GraphEdit::set_show_zoom_label(bool p_hidden) { + show_zoom_label = p_hidden; + zoom_label->set_visible(show_zoom_label); +} + +bool GraphEdit::is_showing_zoom_label() const { + return show_zoom_label; +} + +void GraphEdit::set_show_zoom_buttons(bool p_hidden) { + show_zoom_buttons = p_hidden; + + zoom_minus_button->set_visible(show_zoom_buttons); + zoom_reset_button->set_visible(show_zoom_buttons); + zoom_plus_button->set_visible(show_zoom_buttons); +} + +bool GraphEdit::is_showing_zoom_buttons() const { + return show_zoom_buttons; } -bool GraphEdit::is_arrange_nodes_button_hidden() const { - return arrange_nodes_button_hidden; +void GraphEdit::set_show_grid_buttons(bool p_hidden) { + show_grid_buttons = p_hidden; + + toggle_grid_button->set_visible(show_grid_buttons); + toggle_snapping_button->set_visible(show_grid_buttons); + snapping_distance_spinbox->set_visible(show_grid_buttons); +} + +bool GraphEdit::is_showing_grid_buttons() const { + return show_grid_buttons; +} + +void GraphEdit::set_show_minimap_button(bool p_hidden) { + show_minimap_button = p_hidden; + minimap_button->set_visible(show_minimap_button); +} + +bool GraphEdit::is_showing_minimap_button() const { + return show_minimap_button; +} + +void GraphEdit::set_show_arrange_button(bool p_hidden) { + show_arrange_button = p_hidden; + arrange_button->set_visible(show_arrange_button); +} + +bool GraphEdit::is_showing_arrange_button() const { + return show_arrange_button; } void GraphEdit::_minimap_toggled() { @@ -1814,9 +1856,6 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_zoom_step", "zoom_step"), &GraphEdit::set_zoom_step); ClassDB::bind_method(D_METHOD("get_zoom_step"), &GraphEdit::get_zoom_step); - ClassDB::bind_method(D_METHOD("set_show_zoom_label", "enable"), &GraphEdit::set_show_zoom_label); - ClassDB::bind_method(D_METHOD("is_showing_zoom_label"), &GraphEdit::is_showing_zoom_label); - ClassDB::bind_method(D_METHOD("set_show_grid", "enable"), &GraphEdit::set_show_grid); ClassDB::bind_method(D_METHOD("is_showing_grid"), &GraphEdit::is_showing_grid); @@ -1843,8 +1882,23 @@ void GraphEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_minimap_enabled", "enable"), &GraphEdit::set_minimap_enabled); ClassDB::bind_method(D_METHOD("is_minimap_enabled"), &GraphEdit::is_minimap_enabled); - ClassDB::bind_method(D_METHOD("set_arrange_nodes_button_hidden", "enable"), &GraphEdit::set_arrange_nodes_button_hidden); - ClassDB::bind_method(D_METHOD("is_arrange_nodes_button_hidden"), &GraphEdit::is_arrange_nodes_button_hidden); + ClassDB::bind_method(D_METHOD("set_show_menu", "hidden"), &GraphEdit::set_show_menu); + ClassDB::bind_method(D_METHOD("is_showing_menu"), &GraphEdit::is_showing_menu); + + ClassDB::bind_method(D_METHOD("set_show_zoom_label", "enable"), &GraphEdit::set_show_zoom_label); + ClassDB::bind_method(D_METHOD("is_showing_zoom_label"), &GraphEdit::is_showing_zoom_label); + + ClassDB::bind_method(D_METHOD("set_show_grid_buttons", "hidden"), &GraphEdit::set_show_grid_buttons); + ClassDB::bind_method(D_METHOD("is_showing_grid_buttons"), &GraphEdit::is_showing_grid_buttons); + + ClassDB::bind_method(D_METHOD("set_show_zoom_buttons", "hidden"), &GraphEdit::set_show_zoom_buttons); + ClassDB::bind_method(D_METHOD("is_showing_zoom_buttons"), &GraphEdit::is_showing_zoom_buttons); + + ClassDB::bind_method(D_METHOD("set_show_minimap_button", "hidden"), &GraphEdit::set_show_minimap_button); + ClassDB::bind_method(D_METHOD("is_showing_minimap_button"), &GraphEdit::is_showing_minimap_button); + + ClassDB::bind_method(D_METHOD("set_show_arrange_button", "hidden"), &GraphEdit::set_show_arrange_button); + ClassDB::bind_method(D_METHOD("is_showing_arrange_button"), &GraphEdit::is_showing_arrange_button); ClassDB::bind_method(D_METHOD("set_right_disconnects", "enable"), &GraphEdit::set_right_disconnects); ClassDB::bind_method(D_METHOD("is_right_disconnects_enabled"), &GraphEdit::is_right_disconnects_enabled); @@ -1861,12 +1915,12 @@ void GraphEdit::_bind_methods() { GDVIRTUAL_BIND(_get_connection_line, "from_position", "to_position") GDVIRTUAL_BIND(_is_node_hover_valid, "from_node", "from_port", "to_node", "to_port"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scroll_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_scroll_offset", "get_scroll_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid"), "set_show_grid", "is_showing_grid"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snapping_enabled"), "set_snapping_enabled", "is_snapping_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "snapping_distance", PROPERTY_HINT_NONE, "suffix:px"), "set_snapping_distance", "get_snapping_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "panning_scheme", PROPERTY_HINT_ENUM, "Scroll Zooms,Scroll Pans"), "set_panning_scheme", "get_panning_scheme"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled"); ADD_GROUP("Connection Lines", "connection_lines"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "connection_lines_curvature"), "set_connection_lines_curvature", "get_connection_lines_curvature"); @@ -1878,32 +1932,40 @@ void GraphEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_min"), "set_zoom_min", "get_zoom_min"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_max"), "set_zoom_max", "get_zoom_max"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "zoom_step"), "set_zoom_step", "get_zoom_step"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_label"), "set_show_zoom_label", "is_showing_zoom_label"); ADD_GROUP("Minimap", "minimap_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "minimap_enabled"), "set_minimap_enabled", "is_minimap_enabled"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "minimap_size", PROPERTY_HINT_NONE, "suffix:px"), "set_minimap_size", "get_minimap_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "minimap_opacity"), "set_minimap_opacity", "get_minimap_opacity"); - ADD_GROUP("UI", ""); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arrange_nodes_button_hidden"), "set_arrange_nodes_button_hidden", "is_arrange_nodes_button_hidden"); + ADD_GROUP("Toolbar Menu", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_menu"), "set_show_menu", "is_showing_menu"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_label"), "set_show_zoom_label", "is_showing_zoom_label"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_zoom_buttons"), "set_show_zoom_buttons", "is_showing_zoom_buttons"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_grid_buttons"), "set_show_grid_buttons", "is_showing_grid_buttons"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_minimap_button"), "set_show_minimap_button", "is_showing_minimap_button"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_arrange_button"), "set_show_arrange_button", "is_showing_arrange_button"); ADD_SIGNAL(MethodInfo("connection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"))); ADD_SIGNAL(MethodInfo("disconnection_request", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"))); - ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "position"))); - ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); + ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); + ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); + ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output"))); + ADD_SIGNAL(MethodInfo("connection_drag_ended")); + ADD_SIGNAL(MethodInfo("copy_nodes_request")); ADD_SIGNAL(MethodInfo("paste_nodes_request")); + ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); + ADD_SIGNAL(MethodInfo("delete_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName"))); + ADD_SIGNAL(MethodInfo("node_selected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("node_deselected", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("connection_to_empty", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); - ADD_SIGNAL(MethodInfo("connection_from_empty", PropertyInfo(Variant::STRING_NAME, "to_node"), PropertyInfo(Variant::INT, "to_port"), PropertyInfo(Variant::VECTOR2, "release_position"))); - ADD_SIGNAL(MethodInfo("close_nodes_request", PropertyInfo(Variant::ARRAY, "nodes", PROPERTY_HINT_ARRAY_TYPE, "StringName"))); + + ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2, "position"))); + ADD_SIGNAL(MethodInfo("begin_node_move")); ADD_SIGNAL(MethodInfo("end_node_move")); ADD_SIGNAL(MethodInfo("scroll_offset_changed", PropertyInfo(Variant::VECTOR2, "offset"))); - ADD_SIGNAL(MethodInfo("connection_drag_started", PropertyInfo(Variant::STRING_NAME, "from_node"), PropertyInfo(Variant::INT, "from_port"), PropertyInfo(Variant::BOOL, "is_output"))); - ADD_SIGNAL(MethodInfo("connection_drag_ended")); BIND_ENUM_CONSTANT(SCROLL_ZOOMS); BIND_ENUM_CONSTANT(SCROLL_PANS); @@ -1916,6 +1978,8 @@ void GraphEdit::_bind_methods() { BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_fill); BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, GraphEdit, selection_stroke); + BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, GraphEdit, menu_panel); + BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_in); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_out); BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphEdit, zoom_reset); @@ -1975,84 +2039,106 @@ GraphEdit::GraphEdit() { h_scrollbar->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved)); v_scrollbar->connect("value_changed", callable_mp(this, &GraphEdit::_scroll_moved)); + // Toolbar menu. + + menu_panel = memnew(PanelContainer); + menu_panel->set_visible(show_menu); + top_layer->add_child(menu_panel); + menu_panel->set_position(Vector2(10, 10)); + menu_hbox = memnew(HBoxContainer); - top_layer->add_child(menu_hbox); - menu_hbox->set_position(Vector2(10, 10)); + menu_panel->add_child(menu_hbox); + + // Zoom label and controls. zoom_label = memnew(Label); - menu_hbox->add_child(zoom_label); - zoom_label->set_visible(false); + zoom_label->set_visible(show_zoom_label); zoom_label->set_v_size_flags(Control::SIZE_SHRINK_CENTER); zoom_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER); zoom_label->set_custom_minimum_size(Size2(48, 0)); + menu_hbox->add_child(zoom_label); _update_zoom_label(); zoom_minus_button = memnew(Button); zoom_minus_button->set_theme_type_variation("FlatButton"); - menu_hbox->add_child(zoom_minus_button); + zoom_minus_button->set_visible(show_zoom_buttons); zoom_minus_button->set_tooltip_text(RTR("Zoom Out")); - zoom_minus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus)); zoom_minus_button->set_focus_mode(FOCUS_NONE); + menu_hbox->add_child(zoom_minus_button); + zoom_minus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_minus)); zoom_reset_button = memnew(Button); zoom_reset_button->set_theme_type_variation("FlatButton"); - menu_hbox->add_child(zoom_reset_button); + zoom_reset_button->set_visible(show_zoom_buttons); zoom_reset_button->set_tooltip_text(RTR("Zoom Reset")); - zoom_reset_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset)); zoom_reset_button->set_focus_mode(FOCUS_NONE); + menu_hbox->add_child(zoom_reset_button); + zoom_reset_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_reset)); zoom_plus_button = memnew(Button); zoom_plus_button->set_theme_type_variation("FlatButton"); - menu_hbox->add_child(zoom_plus_button); + zoom_plus_button->set_visible(show_zoom_buttons); zoom_plus_button->set_tooltip_text(RTR("Zoom In")); - zoom_plus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus)); zoom_plus_button->set_focus_mode(FOCUS_NONE); + menu_hbox->add_child(zoom_plus_button); + zoom_plus_button->connect("pressed", callable_mp(this, &GraphEdit::_zoom_plus)); + + // Grid controls. - show_grid_button = memnew(Button); - show_grid_button->set_theme_type_variation("FlatButton"); - show_grid_button->set_toggle_mode(true); - show_grid_button->set_tooltip_text(RTR("Toggle the visual grid.")); - show_grid_button->connect("pressed", callable_mp(this, &GraphEdit::_show_grid_toggled)); - show_grid_button->set_pressed(true); - show_grid_button->set_focus_mode(FOCUS_NONE); - menu_hbox->add_child(show_grid_button); + toggle_grid_button = memnew(Button); + toggle_grid_button->set_theme_type_variation("FlatButton"); + toggle_grid_button->set_visible(show_grid_buttons); + toggle_grid_button->set_toggle_mode(true); + toggle_grid_button->set_pressed(true); + toggle_grid_button->set_tooltip_text(RTR("Toggle the visual grid.")); + toggle_grid_button->set_focus_mode(FOCUS_NONE); + menu_hbox->add_child(toggle_grid_button); + toggle_grid_button->connect("pressed", callable_mp(this, &GraphEdit::_show_grid_toggled)); toggle_snapping_button = memnew(Button); toggle_snapping_button->set_theme_type_variation("FlatButton"); + toggle_snapping_button->set_visible(show_grid_buttons); toggle_snapping_button->set_toggle_mode(true); toggle_snapping_button->set_tooltip_text(RTR("Toggle snapping to the grid.")); - toggle_snapping_button->connect("pressed", callable_mp(this, &GraphEdit::_snapping_toggled)); toggle_snapping_button->set_pressed(snapping_enabled); toggle_snapping_button->set_focus_mode(FOCUS_NONE); menu_hbox->add_child(toggle_snapping_button); + toggle_snapping_button->connect("pressed", callable_mp(this, &GraphEdit::_snapping_toggled)); snapping_distance_spinbox = memnew(SpinBox); + snapping_distance_spinbox->set_visible(show_grid_buttons); snapping_distance_spinbox->set_min(GRID_MIN_SNAPPING_DISTANCE); snapping_distance_spinbox->set_max(GRID_MAX_SNAPPING_DISTANCE); snapping_distance_spinbox->set_step(1); snapping_distance_spinbox->set_value(snapping_distance); snapping_distance_spinbox->set_tooltip_text(RTR("Change the snapping distance.")); - snapping_distance_spinbox->connect("value_changed", callable_mp(this, &GraphEdit::_snapping_distance_changed)); menu_hbox->add_child(snapping_distance_spinbox); + snapping_distance_spinbox->connect("value_changed", callable_mp(this, &GraphEdit::_snapping_distance_changed)); + + // Extra controls. minimap_button = memnew(Button); minimap_button->set_theme_type_variation("FlatButton"); + minimap_button->set_visible(show_minimap_button); minimap_button->set_toggle_mode(true); minimap_button->set_tooltip_text(RTR("Toggle the graph minimap.")); - minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled)); minimap_button->set_pressed(show_grid); minimap_button->set_focus_mode(FOCUS_NONE); menu_hbox->add_child(minimap_button); + minimap_button->connect("pressed", callable_mp(this, &GraphEdit::_minimap_toggled)); + + arrange_button = memnew(Button); + arrange_button->set_theme_type_variation("FlatButton"); + arrange_button->set_visible(show_arrange_button); + arrange_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes)); + arrange_button->set_focus_mode(FOCUS_NONE); + menu_hbox->add_child(arrange_button); + arrange_button->set_tooltip_text(RTR("Automatically arrange selected nodes.")); - layout_button = memnew(Button); - layout_button->set_theme_type_variation("FlatButton"); - menu_hbox->add_child(layout_button); - layout_button->set_tooltip_text(RTR("Automatically arrange selected nodes.")); - layout_button->connect("pressed", callable_mp(this, &GraphEdit::arrange_nodes)); - layout_button->set_focus_mode(FOCUS_NONE); + // Minimap. - Vector2 minimap_size = Vector2(240, 160); - float minimap_opacity = 0.65; + const Vector2 minimap_size = Vector2(240, 160); + const float minimap_opacity = 0.65; minimap = memnew(GraphEditMinimap(this)); top_layer->add_child(minimap); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 6b5698ad41..a7be9ac0b3 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -32,15 +32,17 @@ #define GRAPH_EDIT_H #include "scene/gui/box_container.h" -#include "scene/gui/button.h" #include "scene/gui/graph_node.h" -#include "scene/gui/label.h" -#include "scene/gui/scroll_bar.h" -#include "scene/gui/spin_box.h" +class Button; class GraphEdit; class GraphEditArranger; +class HScrollBar; +class Label; +class PanelContainer; +class SpinBox; class ViewPanner; +class VScrollBar; class GraphEditFilter : public Control { GDCLASS(GraphEditFilter, Control); @@ -154,10 +156,9 @@ private: Button *toggle_snapping_button = nullptr; SpinBox *snapping_distance_spinbox = nullptr; - Button *show_grid_button = nullptr; + Button *toggle_grid_button = nullptr; Button *minimap_button = nullptr; - - Button *layout_button = nullptr; + Button *arrange_button = nullptr; HScrollBar *h_scrollbar = nullptr; VScrollBar *v_scrollbar = nullptr; @@ -165,7 +166,12 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - bool arrange_nodes_button_hidden = false; + bool show_menu = true; + bool show_zoom_label = false; + bool show_grid_buttons = true; + bool show_zoom_buttons = true; + bool show_minimap_button = true; + bool show_arrange_button = true; bool snapping_enabled = true; int snapping_distance = 20; @@ -216,6 +222,7 @@ private: float lines_curvature = 0.5f; bool lines_antialiased = true; + PanelContainer *menu_panel = nullptr; HBoxContainer *menu_hbox = nullptr; Control *connections_layer = nullptr; GraphEditFilter *top_layer = nullptr; @@ -238,6 +245,8 @@ private: Color selection_fill; Color selection_stroke; + Ref<StyleBox> menu_panel; + Ref<Texture2D> zoom_in; Ref<Texture2D> zoom_out; Ref<Texture2D> zoom_reset; @@ -293,6 +302,11 @@ private: bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset); +#ifndef DISABLE_DEPRECATED + bool _is_arrange_nodes_button_hidden_bind_compat_81582() const; + void _set_arrange_nodes_button_hidden_bind_compat_81582(bool p_enable); +#endif + protected: virtual void _update_theme_item_cache() override; @@ -301,6 +315,9 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + static void _bind_compatibility_methods(); +#endif virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); @@ -346,9 +363,6 @@ public: void set_zoom_step(float p_zoom_step); float get_zoom_step() const; - void set_show_zoom_label(bool p_enable); - bool is_showing_zoom_label() const; - void set_minimap_size(Vector2 p_size); Vector2 get_minimap_size() const; void set_minimap_opacity(float p_opacity); @@ -357,8 +371,18 @@ public: void set_minimap_enabled(bool p_enable); bool is_minimap_enabled() const; - void set_arrange_nodes_button_hidden(bool p_enable); - bool is_arrange_nodes_button_hidden() const; + void set_show_menu(bool p_hidden); + bool is_showing_menu() const; + void set_show_zoom_label(bool p_hidden); + bool is_showing_zoom_label() const; + void set_show_grid_buttons(bool p_hidden); + bool is_showing_grid_buttons() const; + void set_show_zoom_buttons(bool p_hidden); + bool is_showing_zoom_buttons() const; + void set_show_minimap_button(bool p_hidden); + bool is_showing_minimap_button() const; + void set_show_arrange_button(bool p_hidden); + bool is_showing_arrange_button() const; GraphEditFilter *get_top_layer() const { return top_layer; } GraphEditMinimap *get_minimap() const { return minimap; } diff --git a/scene/gui/graph_edit_arranger.cpp b/scene/gui/graph_edit_arranger.cpp index 1dc778254b..3f2007f7e0 100644 --- a/scene/gui/graph_edit_arranger.cpp +++ b/scene/gui/graph_edit_arranger.cpp @@ -65,6 +65,9 @@ void GraphEditArranger::arrange_nodes() { float gap_v = 100.0f; float gap_h = 100.0f; + List<GraphEdit::Connection> connection_list; + graph_edit->get_connection_list(&connection_list); + for (int i = graph_edit->get_child_count() - 1; i >= 0; i--) { GraphNode *graph_element = Object::cast_to<GraphNode>(graph_edit->get_child(i)); if (!graph_element) { @@ -74,8 +77,6 @@ void GraphEditArranger::arrange_nodes() { if (graph_element->is_selected() || arrange_entire_graph) { selected_nodes.insert(graph_element->get_name()); HashSet<StringName> s; - List<GraphEdit::Connection> connection_list; - graph_edit->get_connection_list(&connection_list); for (List<GraphEdit::Connection>::Element *E = connection_list.front(); E; E = E->next()) { GraphNode *p_from = Object::cast_to<GraphNode>(node_names[E->get().from_node]); if (E->get().to_node == graph_element->get_name() && (p_from->is_selected() || arrange_entire_graph) && E->get().to_node != E->get().from_node) { @@ -85,12 +86,6 @@ void GraphEditArranger::arrange_nodes() { String s_connection = String(p_from->get_name()) + " " + String(E->get().to_node); StringName _connection(s_connection); Pair<int, int> ports(E->get().from_port, E->get().to_port); - if (port_info.has(_connection)) { - Pair<int, int> p_ports = port_info[_connection]; - if (p_ports.first < ports.first) { - ports = p_ports; - } - } port_info.insert(_connection, ports); } } @@ -216,13 +211,14 @@ int GraphEditArranger::_set_operations(SET_OPERATIONS p_operation, HashSet<Strin return 1; } break; case GraphEditArranger::DIFFERENCE: { - for (HashSet<StringName>::Iterator E = r_u.begin(); E;) { - HashSet<StringName>::Iterator N = E; - ++N; - if (r_v.has(*E)) { - r_u.remove(E); + Vector<StringName> common; + for (const StringName &E : r_u) { + if (r_v.has(E)) { + common.append(E); } - E = N; + } + for (const StringName &E : common) { + r_u.erase(E); } return r_u.size(); } break; @@ -260,9 +256,7 @@ HashMap<int, Vector<StringName>> GraphEditArranger::_layering(const HashSet<Stri selected = true; t.append_array(l[current_layer]); l.insert(current_layer, t); - HashSet<StringName> V; - V.insert(E); - _set_operations(GraphEditArranger::UNION, u, V); + u.insert(E); } } if (!selected) { diff --git a/scene/gui/graph_element.cpp b/scene/gui/graph_element.cpp index ac2cb8bd5d..7fa5b0ceec 100644 --- a/scene/gui/graph_element.cpp +++ b/scene/gui/graph_element.cpp @@ -150,7 +150,7 @@ void GraphElement::gui_input(const Ref<InputEvent> &p_ev) { Ref<InputEventMouseButton> mb = p_ev; if (mb.is_valid()) { - ERR_FAIL_COND_MSG(get_parent_control() == nullptr, "GraphElement must be the child of a GraphEdit node."); + ERR_FAIL_NULL_MSG(get_parent_control(), "GraphElement must be the child of a GraphEdit node."); if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { Vector2 mpos = mb->get_position(); @@ -233,13 +233,15 @@ void GraphElement::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selectable"), "set_selectable", "is_selectable"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selected"), "set_selected", "is_selected"); - ADD_SIGNAL(MethodInfo("position_offset_changed")); ADD_SIGNAL(MethodInfo("node_selected")); ADD_SIGNAL(MethodInfo("node_deselected")); - ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); + ADD_SIGNAL(MethodInfo("raise_request")); - ADD_SIGNAL(MethodInfo("close_request")); + ADD_SIGNAL(MethodInfo("delete_request")); ADD_SIGNAL(MethodInfo("resize_request", PropertyInfo(Variant::VECTOR2, "new_minsize"))); + ADD_SIGNAL(MethodInfo("dragged", PropertyInfo(Variant::VECTOR2, "from"), PropertyInfo(Variant::VECTOR2, "to"))); + ADD_SIGNAL(MethodInfo("position_offset_changed")); + BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, GraphElement, resizer); } diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index fdebca3d28..3b1c1a153f 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -620,7 +620,7 @@ void GraphNode::_port_pos_update() { port_cache.pos = Point2i(edgeofs, vertical_ofs + size.height / 2); port_cache.type = slot_table[i].type_left; port_cache.color = slot_table[i].color_left; - port_cache.slot_index = child->get_index(); // Index with internal nodes included. + port_cache.slot_index = child->get_index(false); left_port_cache.push_back(port_cache); } if (slot_table[i].enable_right) { @@ -628,7 +628,7 @@ void GraphNode::_port_pos_update() { port_cache.pos = Point2i(get_size().width - edgeofs, vertical_ofs + size.height / 2); port_cache.type = slot_table[i].type_right; port_cache.color = slot_table[i].color_right; - port_cache.slot_index = child->get_index(); // Index with internal nodes included. + port_cache.slot_index = child->get_index(false); right_port_cache.push_back(port_cache); } } diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 0d48cb1549..2fbd29b048 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -33,6 +33,7 @@ #include "core/config/project_settings.h" #include "core/string/print_string.h" #include "core/string/translation.h" +#include "scene/gui/container.h" #include "scene/theme/theme_db.h" #include "servers/text_server.h" @@ -44,6 +45,7 @@ void Label::set_autowrap_mode(TextServer::AutowrapMode p_mode) { autowrap_mode = p_mode; lines_dirty = true; queue_redraw(); + update_configuration_warnings(); if (clip || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) { update_minimum_size(); @@ -327,6 +329,19 @@ inline void draw_glyph_outline(const Glyph &p_gl, const RID &p_canvas, const Col PackedStringArray Label::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); + // FIXME: This is not ideal and the sizing model should be fixed, + // but for now we have to warn about this impossible to resolve combination. + // See GH-83546. + if (is_inside_tree() && get_tree()->get_edited_scene_root() != this) { + // If the Label happens to be the root node of the edited scene, we don't need + // to check what its parent is. It's going to be some node from the editor tree + // and it can be a container, but that makes no difference to the user. + Container *parent_container = Object::cast_to<Container>(get_parent_control()); + if (parent_container && autowrap_mode != TextServer::AUTOWRAP_OFF && get_custom_minimum_size() == Size2()) { + warnings.push_back(RTR("Labels with autowrapping enabled must have a custom minimum size configured to work correctly inside a container.")); + } + } + // Ensure that the font can render all of the required glyphs. Ref<Font> font; if (settings.is_valid()) { diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 100e0c4548..5ed1a9d5e3 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -486,6 +486,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (k->is_action("ui_cancel")) { callable_mp((Control *)this, &Control::release_focus).call_deferred(); + accept_event(); return; } @@ -1913,15 +1914,12 @@ bool LineEdit::is_secret() const { } void LineEdit::set_secret_character(const String &p_string) { - // An empty string as the secret character would crash the engine. - // It also wouldn't make sense to use multiple characters as the secret character. - ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); - if (secret_character == p_string) { return; } secret_character = p_string; + update_configuration_warnings(); _shape(); queue_redraw(); } @@ -2265,6 +2263,13 @@ void LineEdit::_emit_text_change() { emit_signal(SNAME("text_changed"), text); text_changed_dirty = false; } +PackedStringArray LineEdit::get_configuration_warnings() const { + PackedStringArray warnings = Control::get_configuration_warnings(); + if (secret_character.length() > 1) { + warnings.push_back("Secret Character property supports only one character. Extra characters will be ignored."); + } + return warnings; +} void LineEdit::_shape() { const Ref<Font> &font = theme_cache.font; @@ -2280,7 +2285,14 @@ void LineEdit::_shape() { if (text.length() == 0 && ime_text.length() == 0) { t = placeholder_translated; } else if (pass) { - t = secret_character.repeat(text.length() + ime_text.length()); + // TODO: Integrate with text server to add support for non-latin scripts. + // Allow secret_character as empty strings, act like if a space was used as a secret character. + String secret = " "; + // Allow values longer than 1 character in the property, but trim characters after the first one. + if (!secret_character.is_empty()) { + secret = secret_character.left(1); + } + t = secret.repeat(text.length() + ime_text.length()); } else { if (ime_text.length() > 0) { t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); @@ -2621,8 +2633,6 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_length", PROPERTY_HINT_RANGE, "0,1000,1,or_greater"), "set_max_length", "get_max_length"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); - ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_to_text_length"), "set_expand_to_text_length_enabled", "is_expand_to_text_length_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "context_menu_enabled"), "set_context_menu_enabled", "is_context_menu_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "virtual_keyboard_enabled"), "set_virtual_keyboard_enabled", "is_virtual_keyboard_enabled"); @@ -2633,7 +2643,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); 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::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); @@ -2645,6 +2655,10 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_force_displayed"), "set_caret_force_displayed", "is_caret_force_displayed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); + ADD_GROUP("Secret", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "secret"), "set_secret", "is_secret"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "secret_character"), "set_secret_character", "get_secret_character"); + ADD_GROUP("BiDi", ""); 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"); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 4a81f90166..993bc727e4 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -385,6 +385,8 @@ public: virtual bool is_text_field() const override; + PackedStringArray get_configuration_warnings() const override; + void show_virtual_keyboard(); LineEdit(const String &p_placeholder = String()); diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp index 5df0bf21a0..1142ba37f5 100644 --- a/scene/gui/link_button.cpp +++ b/scene/gui/link_button.cpp @@ -130,6 +130,10 @@ LinkButton::UnderlineMode LinkButton::get_underline_mode() const { return underline_mode; } +Ref<Font> LinkButton::get_button_font() const { + return theme_cache.font; +} + void LinkButton::pressed() { if (uri.is_empty()) { return; diff --git a/scene/gui/link_button.h b/scene/gui/link_button.h index 6ed47087bf..2faddf2df2 100644 --- a/scene/gui/link_button.h +++ b/scene/gui/link_button.h @@ -104,6 +104,8 @@ public: void set_underline_mode(UnderlineMode p_underline_mode); UnderlineMode get_underline_mode() const; + Ref<Font> get_button_font() const; + LinkButton(const String &p_text = String()); }; diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 13a42d0407..7418ba7333 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -202,52 +202,6 @@ void MenuBar::_popup_visibility_changed(bool p_visible) { } } -void MenuBar::_update_submenu(const String &p_menu_name, PopupMenu *p_child) { - int count = p_child->get_item_count(); - global_menus.insert(p_menu_name); - for (int i = 0; i < count; i++) { - if (p_child->is_item_separator(i)) { - DisplayServer::get_singleton()->global_menu_add_separator(p_menu_name); - } else if (!p_child->get_item_submenu(i).is_empty()) { - Node *n = p_child->get_node_or_null(p_child->get_item_submenu(i)); - ERR_FAIL_NULL_MSG(n, "Item subnode does not exist: '" + p_child->get_item_submenu(i) + "'."); - PopupMenu *pm = Object::cast_to<PopupMenu>(n); - ERR_FAIL_NULL_MSG(pm, "Item subnode is not a PopupMenu: '" + p_child->get_item_submenu(i) + "'."); - - DisplayServer::get_singleton()->global_menu_add_submenu_item(p_menu_name, atr(p_child->get_item_text(i)), p_menu_name + "/" + itos(i)); - _update_submenu(p_menu_name + "/" + itos(i), pm); - } else { - int index = DisplayServer::get_singleton()->global_menu_add_item(p_menu_name, atr(p_child->get_item_text(i)), callable_mp(p_child, &PopupMenu::activate_item), Callable(), i); - - if (p_child->is_item_checkable(i)) { - DisplayServer::get_singleton()->global_menu_set_item_checkable(p_menu_name, index, true); - } - if (p_child->is_item_radio_checkable(i)) { - DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(p_menu_name, index, true); - } - DisplayServer::get_singleton()->global_menu_set_item_checked(p_menu_name, index, p_child->is_item_checked(i)); - DisplayServer::get_singleton()->global_menu_set_item_disabled(p_menu_name, index, p_child->is_item_disabled(i)); - DisplayServer::get_singleton()->global_menu_set_item_max_states(p_menu_name, index, p_child->get_item_max_states(i)); - DisplayServer::get_singleton()->global_menu_set_item_icon(p_menu_name, index, p_child->get_item_icon(i)); - DisplayServer::get_singleton()->global_menu_set_item_state(p_menu_name, index, p_child->get_item_state(i)); - DisplayServer::get_singleton()->global_menu_set_item_indentation_level(p_menu_name, index, p_child->get_item_indent(i)); - DisplayServer::get_singleton()->global_menu_set_item_tooltip(p_menu_name, index, p_child->get_item_tooltip(i)); - if (!p_child->is_item_shortcut_disabled(i) && p_child->get_item_shortcut(i).is_valid() && p_child->get_item_shortcut(i)->has_valid_event()) { - Array events = p_child->get_item_shortcut(i)->get_events(); - for (int j = 0; j < events.size(); j++) { - Ref<InputEventKey> ie = events[j]; - if (ie.is_valid()) { - DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, index, ie->get_keycode_with_modifiers()); - break; - } - } - } else if (p_child->get_item_accelerator(i) != Key::NONE) { - DisplayServer::get_singleton()->global_menu_set_item_accelerator(p_menu_name, i, p_child->get_item_accelerator(i)); - } - } - } -} - bool MenuBar::is_native_menu() const { #ifdef TOOLS_ENABLED if (is_part_of_edited_scene()) { @@ -258,52 +212,67 @@ bool MenuBar::is_native_menu() const { return (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU) && is_native); } -void MenuBar::_clear_menu() { +String MenuBar::bind_global_menu() { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return String(); + } +#endif if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { - return; + return String(); + } + + if (!global_menu_name.is_empty()) { + return global_menu_name; // Already bound. } - // Remove root menu items. - int count = DisplayServer::get_singleton()->global_menu_get_item_count("_main"); - for (int i = count - 1; i >= 0; i--) { - if (global_menus.has(DisplayServer::get_singleton()->global_menu_get_item_submenu("_main", i))) { - DisplayServer::get_singleton()->global_menu_remove_item("_main", i); + DisplayServer *ds = DisplayServer::get_singleton(); + global_menu_name = "__MenuBar#" + itos(get_instance_id()); + + int global_start_idx = -1; + int count = ds->global_menu_get_item_count("_main"); + String prev_tag; + for (int i = 0; i < count; i++) { + String tag = ds->global_menu_get_item_tag("_main", i).operator String().get_slice("#", 1); + if (!tag.is_empty() && tag != prev_tag) { + if (i >= start_index) { + global_start_idx = i; + break; + } } + prev_tag = tag; } - // Erase submenu contents. - for (const String &E : global_menus) { - DisplayServer::get_singleton()->global_menu_clear(E); + if (global_start_idx == -1) { + global_start_idx = count; } - global_menus.clear(); -} -void MenuBar::_update_menu() { - _clear_menu(); + Vector<PopupMenu *> popups = _get_popups(); + for (int i = 0; i < menu_cache.size(); i++) { + String submenu_name = popups[i]->bind_global_menu(); + int index = ds->global_menu_add_submenu_item("_main", menu_cache[i].name, submenu_name, global_start_idx + i); + ds->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(i)); + ds->global_menu_set_item_hidden("_main", index, menu_cache[i].hidden); + ds->global_menu_set_item_disabled("_main", index, menu_cache[i].disabled); + ds->global_menu_set_item_tooltip("_main", index, menu_cache[i].tooltip); + } + + return global_menu_name; +} - if (!is_visible_in_tree()) { +void MenuBar::unbind_global_menu() { + if (global_menu_name.is_empty()) { return; } - int index = start_index; - if (is_native_menu()) { - Vector<PopupMenu *> popups = _get_popups(); - String root_name = "MenuBar<" + String::num_int64((uint64_t)this, 16) + ">"; - for (int i = 0; i < popups.size(); i++) { - if (menu_cache[i].hidden) { - continue; - } - String menu_name = atr(String(popups[i]->get_meta("_menu_name", popups[i]->get_name()))); - - index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", menu_name, root_name + "/" + itos(i), index); - if (menu_cache[i].disabled) { - DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", index, true); - } - _update_submenu(root_name + "/" + itos(i), popups[i]); - index++; - } + DisplayServer *ds = DisplayServer::get_singleton(); + int global_start = _find_global_start_index(); + Vector<PopupMenu *> popups = _get_popups(); + for (int i = menu_cache.size() - 1; i >= 0; i--) { + popups[i]->unbind_global_menu(); + ds->global_menu_remove_item("_main", global_start + i); } - update_minimum_size(); - queue_redraw(); + + global_menu_name = String(); } void MenuBar::_notification(int p_what) { @@ -312,25 +281,43 @@ void MenuBar::_notification(int p_what) { if (get_menu_count() > 0) { _refresh_menu_names(); } + if (is_native_menu()) { + bind_global_menu(); + } } break; case NOTIFICATION_EXIT_TREE: { - _clear_menu(); + unbind_global_menu(); } break; case NOTIFICATION_MOUSE_EXIT: { focused_menu = -1; selected_menu = -1; queue_redraw(); } break; - case NOTIFICATION_TRANSLATION_CHANGED: + case NOTIFICATION_TRANSLATION_CHANGED: { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + int global_start = _find_global_start_index(); + for (int i = 0; i < menu_cache.size(); i++) { + shape(menu_cache.write[i]); + if (is_global) { + ds->global_menu_set_item_text("_main", global_start + i, atr(menu_cache[i].name)); + } + } + } break; case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_THEME_CHANGED: { for (int i = 0; i < menu_cache.size(); i++) { shape(menu_cache.write[i]); } - _update_menu(); } break; case NOTIFICATION_VISIBILITY_CHANGED: { - _update_menu(); + if (is_native_menu()) { + if (is_visible_in_tree()) { + bind_global_menu(); + } else { + unbind_global_menu(); + } + } } break; case NOTIFICATION_DRAW: { if (is_native_menu()) { @@ -512,14 +499,20 @@ void MenuBar::shape(Menu &p_menu) { } void MenuBar::_refresh_menu_names() { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + int global_start = _find_global_start_index(); + Vector<PopupMenu *> popups = _get_popups(); for (int i = 0; i < popups.size(); i++) { if (!popups[i]->has_meta("_menu_name") && String(popups[i]->get_name()) != get_menu_title(i)) { menu_cache.write[i].name = popups[i]->get_name(); shape(menu_cache.write[i]); + if (is_global) { + ds->global_menu_set_item_text("_main", global_start + i, atr(menu_cache[i].name)); + } } } - _update_menu(); } Vector<PopupMenu *> MenuBar::_get_popups() const { @@ -560,11 +553,15 @@ void MenuBar::add_child_notify(Node *p_child) { menu_cache.push_back(menu); p_child->connect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); - p_child->connect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); p_child->connect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(true)); p_child->connect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed).bind(false)); - _update_menu(); + if (!global_menu_name.is_empty()) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, _find_global_start_index() + menu_cache.size() - 1); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(menu_cache.size() - 1)); + } + update_minimum_size(); } void MenuBar::move_child_notify(Node *p_child) { @@ -586,9 +583,20 @@ void MenuBar::move_child_notify(Node *p_child) { } Menu menu = menu_cache[old_idx]; menu_cache.remove_at(old_idx); - menu_cache.insert(get_menu_idx_from_control(pm), menu); + int new_idx = get_menu_idx_from_control(pm); + menu_cache.insert(new_idx, menu); - _update_menu(); + if (!global_menu_name.is_empty()) { + int global_start = _find_global_start_index(); + if (old_idx != -1) { + DisplayServer::get_singleton()->global_menu_remove_item("_main", global_start + old_idx); + } + if (new_idx != -1) { + String submenu_name = pm->bind_global_menu(); + int index = DisplayServer::get_singleton()->global_menu_add_submenu_item("_main", atr(menu.name), submenu_name, global_start + new_idx); + DisplayServer::get_singleton()->global_menu_set_item_tag("_main", index, global_menu_name + "#" + itos(new_idx)); + } + } } void MenuBar::remove_child_notify(Node *p_child) { @@ -603,15 +611,19 @@ void MenuBar::remove_child_notify(Node *p_child) { menu_cache.remove_at(idx); + if (!global_menu_name.is_empty()) { + pm->unbind_global_menu(); + DisplayServer::get_singleton()->global_menu_remove_item("_main", _find_global_start_index() + idx); + } + p_child->remove_meta("_menu_name"); p_child->remove_meta("_menu_tooltip"); p_child->disconnect("renamed", callable_mp(this, &MenuBar::_refresh_menu_names)); - p_child->disconnect("menu_changed", callable_mp(this, &MenuBar::_update_menu)); p_child->disconnect("about_to_popup", callable_mp(this, &MenuBar::_popup_visibility_changed)); p_child->disconnect("popup_hide", callable_mp(this, &MenuBar::_popup_visibility_changed)); - _update_menu(); + update_minimum_size(); } void MenuBar::_bind_methods() { @@ -699,7 +711,8 @@ void MenuBar::set_text_direction(Control::TextDirection p_text_direction) { ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3); if (text_direction != p_text_direction) { text_direction = p_text_direction; - _update_menu(); + update_minimum_size(); + queue_redraw(); } } @@ -710,7 +723,8 @@ Control::TextDirection MenuBar::get_text_direction() const { void MenuBar::set_language(const String &p_language) { if (language != p_language) { language = p_language; - _update_menu(); + update_minimum_size(); + queue_redraw(); } } @@ -732,7 +746,10 @@ bool MenuBar::is_flat() const { void MenuBar::set_start_index(int p_index) { if (start_index != p_index) { start_index = p_index; - _update_menu(); + if (!global_menu_name.is_empty()) { + unbind_global_menu(); + bind_global_menu(); + } } } @@ -742,11 +759,12 @@ int MenuBar::get_start_index() const { void MenuBar::set_prefer_global_menu(bool p_enabled) { if (is_native != p_enabled) { + is_native = p_enabled; if (is_native) { - _clear_menu(); + bind_global_menu(); + } else { + unbind_global_menu(); } - is_native = p_enabled; - _update_menu(); } } @@ -790,7 +808,10 @@ void MenuBar::set_menu_title(int p_menu, const String &p_title) { } menu_cache.write[p_menu].name = p_title; shape(menu_cache.write[p_menu]); - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_text("_main", _find_global_start_index() + p_menu, atr(menu_cache[p_menu].name)); + } + update_minimum_size(); } String MenuBar::get_menu_title(int p_menu) const { @@ -802,7 +823,10 @@ void MenuBar::set_menu_tooltip(int p_menu, const String &p_tooltip) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); PopupMenu *pm = get_menu_popup(p_menu); pm->set_meta("_menu_tooltip", p_tooltip); - menu_cache.write[p_menu].name = p_tooltip; + menu_cache.write[p_menu].tooltip = p_tooltip; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tooltip("_main", _find_global_start_index() + p_menu, p_tooltip); + } } String MenuBar::get_menu_tooltip(int p_menu) const { @@ -813,7 +837,9 @@ String MenuBar::get_menu_tooltip(int p_menu) const { void MenuBar::set_menu_disabled(int p_menu, bool p_disabled) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].disabled = p_disabled; - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_disabled("_main", _find_global_start_index() + p_menu, p_disabled); + } } bool MenuBar::is_menu_disabled(int p_menu) const { @@ -824,7 +850,10 @@ bool MenuBar::is_menu_disabled(int p_menu) const { void MenuBar::set_menu_hidden(int p_menu, bool p_hidden) { ERR_FAIL_INDEX(p_menu, menu_cache.size()); menu_cache.write[p_menu].hidden = p_hidden; - _update_menu(); + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_hidden("_main", _find_global_start_index() + p_menu, p_hidden); + } + update_minimum_size(); } bool MenuBar::is_menu_hidden(int p_menu) const { diff --git a/scene/gui/menu_bar.h b/scene/gui/menu_bar.h index 4d6e76d9b6..ba4df5f229 100644 --- a/scene/gui/menu_bar.h +++ b/scene/gui/menu_bar.h @@ -66,7 +66,6 @@ class MenuBar : public Control { } }; Vector<Menu> menu_cache; - HashSet<String> global_menus; int focused_menu = -1; int selected_menu = -1; @@ -114,9 +113,23 @@ class MenuBar : public Control { void _open_popup(int p_index, bool p_focus_item = false); void _popup_visibility_changed(bool p_visible); - void _update_submenu(const String &p_menu_name, PopupMenu *p_child); - void _clear_menu(); - void _update_menu(); + + String global_menu_name; + + int _find_global_start_index() { + if (global_menu_name.is_empty()) { + return -1; + } + + DisplayServer *ds = DisplayServer::get_singleton(); + int count = ds->global_menu_get_item_count("_main"); + for (int i = 0; i < count; i++) { + if (ds->global_menu_get_item_tag("_main", i).operator String().begins_with(global_menu_name)) { + return i; + } + } + return -1; + } protected: virtual void shortcut_input(const Ref<InputEvent> &p_event) override; @@ -130,6 +143,9 @@ protected: public: virtual void gui_input(const Ref<InputEvent> &p_event) override; + String bind_global_menu(); + void unbind_global_menu(); + void set_switch_on_hover(bool p_enabled); bool is_switch_on_hover(); void set_disable_shortcuts(bool p_disabled); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 54fd8b8745..d6b8dd0202 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -40,6 +40,86 @@ #include "scene/gui/menu_bar.h" #include "scene/theme/theme_db.h" +String PopupMenu::bind_global_menu() { +#ifdef TOOLS_ENABLED + if (is_part_of_edited_scene()) { + return String(); + } +#endif + if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_GLOBAL_MENU)) { + return String(); + } + + if (!global_menu_name.is_empty()) { + return global_menu_name; // Already bound; + } + + DisplayServer *ds = DisplayServer::get_singleton(); + global_menu_name = "__PopupMenu#" + itos(get_instance_id()); + ds->global_menu_set_popup_callbacks(global_menu_name, callable_mp(this, &PopupMenu::_about_to_popup), callable_mp(this, &PopupMenu::_about_to_close)); + for (int i = 0; i < items.size(); i++) { + Item &item = items.write[i]; + if (item.separator) { + ds->global_menu_add_separator(global_menu_name); + } else { + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), i); + if (!item.submenu.is_empty()) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); + if (pm) { + String submenu_name = pm->bind_global_menu(); + ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name); + item.submenu_bound = true; + } + } + if (item.checkable_type == Item::CHECKABLE_TYPE_CHECK_BOX) { + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } else if (item.checkable_type == Item::CHECKABLE_TYPE_RADIO_BUTTON) { + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + ds->global_menu_set_item_checked(global_menu_name, index, item.checked); + ds->global_menu_set_item_disabled(global_menu_name, index, item.disabled); + ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_state(global_menu_name, index, item.state); + ds->global_menu_set_item_indentation_level(global_menu_name, index, item.indent); + ds->global_menu_set_item_tooltip(global_menu_name, index, item.tooltip); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } else if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + } + } + return global_menu_name; +} + +void PopupMenu::unbind_global_menu() { + if (global_menu_name.is_empty()) { + return; + } + + for (int i = 0; i < items.size(); i++) { + Item &item = items.write[i]; + if (!item.submenu.is_empty()) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); + if (pm) { + pm->unbind_global_menu(); + } + item.submenu_bound = false; + } + } + DisplayServer::get_singleton()->global_menu_clear(global_menu_name); + + global_menu_name = String(); +} + String PopupMenu::_get_accel_text(const Item &p_item) const { if (p_item.shortcut.is_valid()) { return p_item.shortcut->get_as_text(); @@ -631,9 +711,9 @@ void PopupMenu::_draw_items() { // Separator item_ofs.x += items[i].indent * theme_cache.indent; if (items[i].separator) { - if (!text.is_empty() || !items[i].icon.is_null()) { + if (!text.is_empty() || items[i].icon.is_valid()) { int content_size = items[i].text_buf->get_size().width + theme_cache.h_separation * 2; - if (!items[i].icon.is_null()) { + if (items[i].icon.is_valid()) { content_size += icon_size.width + theme_cache.h_separation; } @@ -662,7 +742,9 @@ void PopupMenu::_draw_items() { icon_color *= items[i].icon_modulate; // For non-separator items, add some padding for the content. - item_ofs.x += theme_cache.item_start_padding; + if (!items[i].separator) { + item_ofs.x += theme_cache.item_start_padding; + } // Checkboxes if (items[i].checkable_type && !items[i].separator) { @@ -678,7 +760,7 @@ 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 (items[i].icon.is_valid()) { const Point2 icon_offset = Point2(0, Math::floor((h - icon_size.height) / 2.0)); Point2 icon_pos; @@ -689,6 +771,7 @@ void PopupMenu::_draw_items() { icon_pos = Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y); } else { icon_pos = item_ofs + Size2(separator_ofs, 0); + separator_ofs += icon_size.width + theme_cache.h_separation; } } else { if (rtl) { @@ -714,9 +797,6 @@ void PopupMenu::_draw_items() { if (items[i].separator) { if (!text.is_empty()) { 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 + theme_cache.h_separation; - } if (theme_cache.font_separator_outline_size > 0 && theme_cache.font_separator_outline_color.a > 0) { items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_separator_outline_size, theme_cache.font_separator_outline_color); @@ -821,11 +901,17 @@ void PopupMenu::_menu_changed() { void PopupMenu::add_child_notify(Node *p_child) { Window::add_child_notify(p_child); - PopupMenu *pm = Object::cast_to<PopupMenu>(p_child); - if (!pm) { - return; + if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) { + String node_name = p_child->get_name(); + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(node_name)); + for (int i = 0; i < items.size(); i++) { + if (items[i].submenu == node_name) { + String submenu_name = pm->bind_global_menu(); + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, submenu_name); + items.write[i].submenu_bound = true; + } + } } - p_child->connect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); _menu_changed(); } @@ -836,7 +922,16 @@ void PopupMenu::remove_child_notify(Node *p_child) { if (!pm) { return; } - p_child->disconnect("menu_changed", callable_mp(this, &PopupMenu::_menu_changed)); + if (Object::cast_to<PopupMenu>(p_child) && !global_menu_name.is_empty()) { + String node_name = p_child->get_name(); + for (int i = 0; i < items.size(); i++) { + if (items[i].submenu == node_name) { + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, i, String()); + items.write[i].submenu_bound = false; + } + } + pm->unbind_global_menu(); + } _menu_changed(); } @@ -857,9 +952,15 @@ void PopupMenu::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); for (int i = 0; i < items.size(); i++) { - items.write[i].xl_text = atr(items[i].text); - items.write[i].dirty = true; + Item &item = items.write[i]; + item.xl_text = atr(item.text); + item.dirty = true; + if (is_global) { + ds->global_menu_set_item_text(global_menu_name, i, item.xl_text); + } _shape_item(i); } @@ -1007,10 +1108,12 @@ void PopupMenu::_notification(int p_what) { } // Set margin on the margin container + margin_container->begin_bulk_theme_override(); margin_container->add_theme_constant_override("margin_left", theme_cache.panel_style->get_margin(Side::SIDE_LEFT)); margin_container->add_theme_constant_override("margin_top", theme_cache.panel_style->get_margin(Side::SIDE_TOP)); margin_container->add_theme_constant_override("margin_right", theme_cache.panel_style->get_margin(Side::SIDE_RIGHT)); margin_container->add_theme_constant_override("margin_bottom", theme_cache.panel_style->get_margin(Side::SIDE_BOTTOM)); + margin_container->end_bulk_theme_override(); } } break; } @@ -1031,6 +1134,14 @@ void PopupMenu::add_item(const String &p_label, int p_id, Key p_accel) { ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel); items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + } + _shape_item(items.size() - 1); control->queue_redraw(); @@ -1045,6 +1156,15 @@ void PopupMenu::add_icon_item(const Ref<Texture2D> &p_icon, const String &p_labe item.icon = p_icon; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + } + _shape_item(items.size() - 1); control->queue_redraw(); @@ -1059,10 +1179,20 @@ void PopupMenu::add_check_item(const String &p_label, int p_id, Key p_accel) { item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1073,10 +1203,22 @@ void PopupMenu::add_icon_check_item(const Ref<Texture2D> &p_icon, const String & item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); + _menu_changed(); } void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_accel) { @@ -1085,10 +1227,20 @@ void PopupMenu::add_radio_check_item(const String &p_label, int p_id, Key p_acce item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1099,10 +1251,21 @@ void PopupMenu::add_icon_radio_check_item(const Ref<Texture2D> &p_icon, const St item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1113,11 +1276,22 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int item.state = p_default_state; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (item.accel != Key::NONE) { + ds->global_menu_set_item_accelerator(global_menu_name, index, item.accel); + } + ds->global_menu_set_item_max_states(global_menu_name, index, item.max_states); + ds->global_menu_set_item_state(global_menu_name, index, item.state); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); _menu_changed(); + notify_property_list_changed(); } #define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \ @@ -1135,10 +1309,26 @@ void PopupMenu::add_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bool p_g ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo); items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1148,10 +1338,27 @@ void PopupMenu::add_icon_shortcut(const Ref<Texture2D> &p_icon, const Ref<Shortc item.icon = p_icon; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1161,10 +1368,27 @@ void PopupMenu::add_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_id, bo item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1175,10 +1399,28 @@ void PopupMenu::add_icon_check_shortcut(const Ref<Texture2D> &p_icon, const Ref< item.checkable_type = Item::CHECKABLE_TYPE_CHECK_BOX; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1188,10 +1430,27 @@ void PopupMenu::add_radio_check_shortcut(const Ref<Shortcut> &p_shortcut, int p_ item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1202,14 +1461,37 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons item.checkable_type = Item::CHECKABLE_TYPE_RADIO_BUTTON; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + if (!item.shortcut_is_disabled && item.shortcut.is_valid() && item.shortcut->has_valid_event()) { + Array events = item.shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, index, ie->get_keycode_with_modifiers()); + break; + } + } + } + ds->global_menu_set_item_icon(global_menu_name, index, item.icon); + ds->global_menu_set_item_radio_checkable(global_menu_name, index, true); + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { + String submenu_name_safe = p_submenu.replace("@", "_"); // Allow special characters for auto-generated names. + if (submenu_name_safe.validate_node_name() != submenu_name_safe) { + ERR_FAIL_MSG(vformat("Invalid node name '%s' for a submenu, the following characters are not allowed:\n%s", p_submenu, String::get_invalid_node_name_characters(true))); + } + Item item; item.text = p_label; item.xl_text = atr(p_label); @@ -1217,10 +1499,22 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, item.submenu = p_submenu; items.push_back(item); + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + int index = ds->global_menu_add_item(global_menu_name, item.xl_text, callable_mp(this, &PopupMenu::activate_item), Callable(), items.size() - 1); + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); // Find first menu with this name. + if (pm) { + String submenu_name = pm->bind_global_menu(); + ds->global_menu_set_item_submenu(global_menu_name, index, submenu_name); + items.write[index].submenu_bound = true; + } + } + _shape_item(items.size() - 1); control->queue_redraw(); child_controls_changed(); + notify_property_list_changed(); _menu_changed(); } @@ -1240,6 +1534,10 @@ void PopupMenu::set_item_text(int p_idx, const String &p_text) { items.write[p_idx].text = p_text; items.write[p_idx].xl_text = atr(p_text); items.write[p_idx].dirty = true; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_text(global_menu_name, p_idx, items[p_idx].xl_text); + } _shape_item(p_idx); control->queue_redraw(); @@ -1284,6 +1582,10 @@ void PopupMenu::set_item_icon(int p_idx, const Ref<Texture2D> &p_icon) { items.write[p_idx].icon = p_icon; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_icon(global_menu_name, p_idx, items[p_idx].icon); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1332,6 +1634,10 @@ void PopupMenu::set_item_checked(int p_idx, bool p_checked) { items.write[p_idx].checked = p_checked; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, p_checked); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1349,6 +1655,10 @@ void PopupMenu::set_item_id(int p_idx, int p_id) { items.write[p_idx].id = p_id; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tag(global_menu_name, p_idx, p_id); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1367,6 +1677,10 @@ void PopupMenu::set_item_accelerator(int p_idx, Key p_accel) { items.write[p_idx].accel = p_accel; items.write[p_idx].dirty = true; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_accelerator(global_menu_name, p_idx, p_accel); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1383,7 +1697,6 @@ void PopupMenu::set_item_metadata(int p_idx, const Variant &p_meta) { } items.write[p_idx].metadata = p_meta; - control->queue_redraw(); child_controls_changed(); _menu_changed(); } @@ -1399,6 +1712,11 @@ void PopupMenu::set_item_disabled(int p_idx, bool p_disabled) { } items.write[p_idx].disabled = p_disabled; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_disabled(global_menu_name, p_idx, p_disabled); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1414,7 +1732,30 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { return; } + if (!global_menu_name.is_empty()) { + if (items[p_idx].submenu_bound) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu)); + if (pm) { + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, String()); + pm->unbind_global_menu(); + } + items.write[p_idx].submenu_bound = false; + } + } + items.write[p_idx].submenu = p_submenu; + + if (!global_menu_name.is_empty()) { + if (!items[p_idx].submenu.is_empty()) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(items[p_idx].submenu)); + if (pm) { + String submenu_name = pm->bind_global_menu(); + DisplayServer::get_singleton()->global_menu_set_item_submenu(global_menu_name, p_idx, submenu_name); + items.write[p_idx].submenu_bound = true; + } + } + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1423,6 +1764,11 @@ void PopupMenu::set_item_submenu(int p_idx, const String &p_submenu) { void PopupMenu::toggle_item_checked(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].checked = !items[p_idx].checked; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checked(global_menu_name, p_idx, items[p_idx].checked); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1569,6 +1915,11 @@ void PopupMenu::set_item_as_checkable(int p_idx, bool p_checkable) { } items.write[p_idx].checkable_type = p_checkable ? Item::CHECKABLE_TYPE_CHECK_BOX : Item::CHECKABLE_TYPE_NONE; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_checkable(global_menu_name, p_idx, p_checkable); + } + control->queue_redraw(); _menu_changed(); } @@ -1585,6 +1936,11 @@ void PopupMenu::set_item_as_radio_checkable(int p_idx, bool p_radio_checkable) { } items.write[p_idx].checkable_type = p_radio_checkable ? Item::CHECKABLE_TYPE_RADIO_BUTTON : Item::CHECKABLE_TYPE_NONE; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_radio_checkable(global_menu_name, p_idx, p_radio_checkable); + } + control->queue_redraw(); _menu_changed(); } @@ -1600,6 +1956,11 @@ void PopupMenu::set_item_tooltip(int p_idx, const String &p_tooltip) { } items.write[p_idx].tooltip = p_tooltip; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_tooltip(global_menu_name, p_idx, p_tooltip); + } + control->queue_redraw(); _menu_changed(); } @@ -1625,6 +1986,21 @@ void PopupMenu::set_item_shortcut(int p_idx, const Ref<Shortcut> &p_shortcut, bo _ref_shortcut(items[p_idx].shortcut); } + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { + Array events = items[p_idx].shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + control->queue_redraw(); _menu_changed(); } @@ -1640,6 +2016,10 @@ void PopupMenu::set_item_indent(int p_idx, int p_indent) { } items.write[p_idx].indent = p_indent; + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_indentation_level(global_menu_name, p_idx, p_indent); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1656,6 +2036,11 @@ void PopupMenu::set_item_multistate(int p_idx, int p_state) { } items.write[p_idx].state = p_state; + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, p_state); + } + control->queue_redraw(); _menu_changed(); } @@ -1671,6 +2056,22 @@ void PopupMenu::set_item_shortcut_disabled(int p_idx, bool p_disabled) { } items.write[p_idx].shortcut_is_disabled = p_disabled; + + if (!global_menu_name.is_empty()) { + DisplayServer *ds = DisplayServer::get_singleton(); + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, Key::NONE); + if (!items[p_idx].shortcut_is_disabled && items[p_idx].shortcut.is_valid() && items[p_idx].shortcut->has_valid_event()) { + Array events = items[p_idx].shortcut->get_events(); + for (int j = 0; j < events.size(); j++) { + Ref<InputEventKey> ie = events[j]; + if (ie.is_valid()) { + ds->global_menu_set_item_accelerator(global_menu_name, p_idx, ie->get_keycode_with_modifiers()); + break; + } + } + } + } + control->queue_redraw(); _menu_changed(); } @@ -1686,6 +2087,10 @@ void PopupMenu::toggle_item_multistate(int p_idx) { items.write[p_idx].state = 0; } + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_set_item_state(global_menu_name, p_idx, items[p_idx].state); + } + control->queue_redraw(); _menu_changed(); } @@ -1739,11 +2144,23 @@ void PopupMenu::set_item_count(int p_count) { return; } + DisplayServer *ds = DisplayServer::get_singleton(); + bool is_global = !global_menu_name.is_empty(); + + if (is_global && prev_size > p_count) { + for (int i = prev_size - 1; i >= p_count; i--) { + ds->global_menu_remove_item(global_menu_name, i); + } + } + items.resize(p_count); if (prev_size < p_count) { for (int i = prev_size; i < p_count; i++) { items.write[i].id = i; + if (is_global) { + ds->global_menu_add_item(global_menu_name, String(), callable_mp(this, &PopupMenu::activate_item), Callable(), i); + } } } @@ -1760,18 +2177,21 @@ int PopupMenu::get_item_count() const { void PopupMenu::scroll_to_item(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); - // Scroll item into view (upwards). - if (items[p_idx]._ofs_cache - scroll_container->get_v_scroll() < -control->get_position().y) { - scroll_container->set_v_scroll(items[p_idx]._ofs_cache + control->get_position().y); - } + // Calculate the position of the item relative to the visible area. + int item_y = items[p_idx]._ofs_cache; + int visible_height = scroll_container->get_size().height; + int relative_y = item_y - scroll_container->get_v_scroll(); - // Scroll item into view (downwards). - if (items[p_idx]._ofs_cache + items[p_idx]._height_cache - scroll_container->get_v_scroll() > -control->get_position().y + scroll_container->get_size().height) { - scroll_container->set_v_scroll(items[p_idx]._ofs_cache + items[p_idx]._height_cache + control->get_position().y); + // If item is not fully visible, adjust scroll. + if (relative_y < 0) { + scroll_container->set_v_scroll(item_y); + } else if (relative_y + items[p_idx]._height_cache > visible_height) { + scroll_container->set_v_scroll(item_y + items[p_idx]._height_cache - visible_height); } } bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only) { + ERR_FAIL_COND_V(p_event.is_null(), false); Key code = Key::NONE; Ref<InputEventKey> k = p_event; @@ -1828,6 +2248,16 @@ bool PopupMenu::activate_item_by_event(const Ref<InputEvent> &p_event, bool p_fo return false; } +void PopupMenu::_about_to_popup() { + ERR_MAIN_THREAD_GUARD; + emit_signal(SNAME("about_to_popup")); +} + +void PopupMenu::_about_to_close() { + ERR_MAIN_THREAD_GUARD; + emit_signal(SNAME("popup_hide")); +} + void PopupMenu::activate_item(int p_idx) { ERR_FAIL_INDEX(p_idx, items.size()); ERR_FAIL_COND(items[p_idx].separator); @@ -1890,6 +2320,11 @@ void PopupMenu::remove_item(int p_idx) { } items.remove_at(p_idx); + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_remove_item(global_menu_name, p_idx); + } + control->queue_redraw(); child_controls_changed(); _menu_changed(); @@ -1904,6 +2339,11 @@ void PopupMenu::add_separator(const String &p_text, int p_id) { sep.xl_text = atr(p_text); } items.push_back(sep); + + if (!global_menu_name.is_empty()) { + DisplayServer::get_singleton()->global_menu_add_separator(global_menu_name); + } + control->queue_redraw(); _menu_changed(); } @@ -1922,7 +2362,22 @@ void PopupMenu::clear(bool p_free_submenus) { } } } + + if (!global_menu_name.is_empty()) { + for (int i = 0; i < items.size(); i++) { + Item &item = items.write[i]; + if (!item.submenu.is_empty()) { + PopupMenu *pm = Object::cast_to<PopupMenu>(get_node_or_null(item.submenu)); + if (pm) { + pm->unbind_global_menu(); + } + item.submenu_bound = false; + } + } + DisplayServer::get_singleton()->global_menu_clear(global_menu_name); + } items.clear(); + mouse_over = -1; control->queue_redraw(); child_controls_changed(); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index f123d08400..5d5f4a8322 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -75,6 +75,7 @@ class PopupMenu : public Popup { bool shortcut_is_global = false; bool shortcut_is_disabled = false; bool allow_echo = false; + bool submenu_bound = false; // Returns (0,0) if icon is null. Size2 get_icon_size() const { @@ -88,6 +89,8 @@ class PopupMenu : public Popup { } }; + String global_menu_name; + bool close_allowed = false; bool activated_by_keyboard = false; @@ -213,6 +216,9 @@ public: virtual void _parent_focused() override; + String bind_global_menu(); + void unbind_global_menu(); + void add_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_icon_item(const Ref<Texture2D> &p_icon, const String &p_label, int p_id = -1, Key p_accel = Key::NONE); void add_check_item(const String &p_label, int p_id = -1, Key p_accel = Key::NONE); @@ -293,6 +299,9 @@ public: bool activate_item_by_event(const Ref<InputEvent> &p_event, bool p_for_global_only = false); void activate_item(int p_idx); + void _about_to_popup(); + void _about_to_close(); + void remove_item(int p_idx); void add_separator(const String &p_text = String(), int p_id = -1); diff --git a/scene/gui/range.h b/scene/gui/range.h index 9b4f0707e6..b1c2446ded 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -64,6 +64,7 @@ class Range : public Control { protected: virtual void _value_changed(double p_value); + void _notify_shared_value_changed() { shared->emit_value_changed(); }; static void _bind_methods(); diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index b4cd201e6a..30a468dfc5 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -307,7 +307,7 @@ float RichTextLabel::_resize_line(ItemFrame *p_frame, int p_line, const Ref<Font Size2 img_size = img->size; if (img->size_in_percent) { img_size = _get_image_size(img->image, p_width * img->rq_size.width / 100.f, p_width * img->rq_size.height / 100.f, img->region); - l.text_buf->resize_object((uint64_t)it, img_size, img->inline_align, 1); + l.text_buf->resize_object((uint64_t)it, img_size, img->inline_align); } } break; case ITEM_TABLE: { @@ -830,37 +830,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o prefix = segment + prefix; } } - if (!prefix.is_empty()) { - Ref<Font> font = theme_cache.normal_font; - int font_size = theme_cache.normal_font_size; - - ItemFont *font_it = _find_font(l.from); - if (font_it) { - if (font_it->font.is_valid()) { - font = font_it->font; - } - if (font_it->font_size > 0) { - font_size = font_it->font_size; - } - } - ItemFontSize *font_size_it = _find_font_size(l.from); - if (font_size_it && font_size_it->font_size > 0) { - font_size = font_size_it->font_size; - } - if (rtl) { - float offx = 0.0f; - if (!lrtl && p_frame == main) { // Skip Scrollbar. - offx -= scroll_w; - } - font->draw_string(ci, p_ofs + Vector2(p_width - l.offset.x + offx, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); - } else { - float offx = 0.0f; - if (lrtl && p_frame == main) { // Skip Scrollbar. - offx += scroll_w; - } - font->draw_string(ci, p_ofs + Vector2(offx, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); - } - } // Draw dropcap. int dc_lines = l.text_buf->get_dropcap_lines(); @@ -924,6 +893,30 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } break; } + if (!prefix.is_empty() && line == 0) { + Ref<Font> font = theme_cache.normal_font; + int font_size = theme_cache.normal_font_size; + + ItemFont *font_it = _find_font(l.from); + if (font_it) { + if (font_it->font.is_valid()) { + font = font_it->font; + } + if (font_it->font_size > 0) { + font_size = font_it->font_size; + } + } + ItemFontSize *font_size_it = _find_font_size(l.from); + if (font_size_it && font_size_it->font_size > 0) { + font_size = font_size_it->font_size; + } + if (rtl) { + font->draw_string(ci, p_ofs + Vector2(off.x + length, l.text_buf->get_line_ascent(0)), " " + prefix, HORIZONTAL_ALIGNMENT_LEFT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + } else { + font->draw_string(ci, p_ofs + Vector2(off.x - l.offset.x, l.text_buf->get_line_ascent(0)), prefix + " ", HORIZONTAL_ALIGNMENT_RIGHT, l.offset.x, font_size, _find_color(l.from, p_base_color)); + } + } + if (line <= dc_lines) { if (rtl) { off.x -= h_off; @@ -1143,8 +1136,7 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { fx_offset = fx_offset.round(); } - - Vector2i char_off = char_xform.get_origin(); + Vector2 char_off = char_xform.get_origin(); // Draw glyph outlines. const Color modulated_outline_color = font_outline_color * Color(1, 1, 1, font_color.a); @@ -1220,7 +1212,14 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o Item *it = _get_item_at_pos(it_from, it_to, glyphs[i].start); Color font_color = _find_color(it, p_base_color); if (_find_underline(it) || (_find_meta(it, nullptr) && underline_meta)) { - if (!ul_started) { + if (ul_started && font_color != ul_color) { + float y_off = TS->shaped_text_get_underline_position(rid); + float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale); + draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width); + ul_start = p_ofs + Vector2(off.x, off.y); + ul_color = font_color; + ul_color.a *= 0.5; + } else if (!ul_started) { ul_started = true; ul_start = p_ofs + Vector2(off.x, off.y); ul_color = font_color; @@ -1233,7 +1232,14 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o draw_line(ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), ul_color, underline_width); } if (_find_hint(it, nullptr) && underline_hint) { - if (!dot_ul_started) { + if (dot_ul_started && font_color != dot_ul_color) { + float y_off = TS->shaped_text_get_underline_position(rid); + float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale); + draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); + dot_ul_start = p_ofs + Vector2(off.x, off.y); + dot_ul_color = font_color; + dot_ul_color.a *= 0.5; + } else if (!dot_ul_started) { dot_ul_started = true; dot_ul_start = p_ofs + Vector2(off.x, off.y); dot_ul_color = font_color; @@ -1246,7 +1252,14 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o draw_dashed_line(dot_ul_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), dot_ul_color, underline_width, MAX(2.0, underline_width * 2)); } if (_find_strikethrough(it)) { - if (!st_started) { + if (st_started && font_color != st_color) { + float y_off = -TS->shaped_text_get_ascent(rid) + TS->shaped_text_get_size(rid).y / 2; + float underline_width = MAX(1.0, TS->shaped_text_get_underline_thickness(rid) * theme_cache.base_scale); + draw_line(st_start + Vector2(0, y_off), p_ofs + Vector2(off.x, off.y + y_off), st_color, underline_width); + st_start = p_ofs + Vector2(off.x, off.y); + st_color = font_color; + st_color.a *= 0.5; + } else if (!st_started) { st_started = true; st_start = p_ofs + Vector2(off.x, off.y); st_color = font_color; @@ -1389,8 +1402,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (is_inside_tree() && get_viewport()->is_snap_2d_transforms_to_pixel_enabled()) { fx_offset = fx_offset.round(); } + Vector2 char_off = char_xform.get_origin(); - Vector2i char_off = char_xform.get_origin(); Transform2D char_reverse_xform; char_reverse_xform.set_origin(-char_off); char_xform = char_xform * char_reverse_xform; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2931b5be91..1310cac2c7 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -468,22 +468,6 @@ double ScrollBar::get_area_size() const { } } -double ScrollBar::get_area_offset() const { - double ofs = 0.0; - - if (orientation == VERTICAL) { - ofs += theme_cache.scroll_offset_style->get_margin(SIDE_TOP); - ofs += theme_cache.decrement_icon->get_height(); - } - - if (orientation == HORIZONTAL) { - ofs += theme_cache.scroll_offset_style->get_margin(SIDE_LEFT); - ofs += theme_cache.decrement_icon->get_width(); - } - - return ofs; -} - double ScrollBar::get_grabber_offset() const { return (get_area_size()) * get_as_ratio(); } @@ -639,7 +623,6 @@ void ScrollBar::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_style, "scroll"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_focus_style, "scroll_focus"); - BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_offset_style, "hscroll"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_style, "grabber"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_hl_style, "grabber_highlight"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_pressed_style, "grabber_pressed"); diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index aacf2060b8..deadbb53d6 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -63,7 +63,6 @@ class ScrollBar : public Range { double get_grabber_size() const; double get_grabber_min_size() const; double get_area_size() const; - double get_area_offset() const; double get_grabber_offset() const; static void set_can_focus_by_default(bool p_can_focus); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index b1a2f8017e..8bb8eb1d30 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -68,15 +68,18 @@ void Slider::gui_input(const Ref<InputEvent> &p_event) { double grab_width = (double)grabber->get_width(); double grab_height = (double)grabber->get_height(); double max = orientation == VERTICAL ? get_size().height - grab_height : get_size().width - grab_width; + set_block_signals(true); if (orientation == VERTICAL) { set_as_ratio(1 - (((double)grab.pos - (grab_height / 2.0)) / max)); } else { set_as_ratio(((double)grab.pos - (grab_width / 2.0)) / max); } + set_block_signals(false); grab.active = true; grab.uvalue = get_as_ratio(); emit_signal(SNAME("drag_started")); + _notify_shared_value_changed(); } else { grab.active = false; diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 26dbe1cb0c..bd549a6e4a 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -40,7 +40,7 @@ Size2 SpinBox::get_minimum_size() const { return ms; } -void SpinBox::_update_text() { +void SpinBox::_update_text(bool p_keep_line_edit) { String value = String::num(get_value(), Math::range_step_decimals(get_step())); if (is_localizing_numeral_system()) { value = TS->format_number(value); @@ -55,7 +55,12 @@ void SpinBox::_update_text() { } } + if (p_keep_line_edit && value == last_updated_text && value != line_edit->get_text()) { + return; + } + line_edit->set_text_with_selection(value); + last_updated_text = value; } void SpinBox::_text_submitted(const String &p_string) { @@ -245,7 +250,7 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { void SpinBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - _update_text(); + _update_text(true); _adjust_width_for_icon(theme_cache.updown_icon); RID ci = get_canvas_item(); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index bbb1db637a..4d49626d71 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -46,12 +46,13 @@ class SpinBox : public Range { void _range_click_timeout(); void _release_mouse(); - void _update_text(); + void _update_text(bool p_keep_line_edit = false); void _text_submitted(const String &p_string); void _text_changed(const String &p_string); String prefix; String suffix; + String last_updated_text; double custom_arrow_step = 0.0; void _line_edit_input(const Ref<InputEvent> &p_event); diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 851a94b32f..0d33774e20 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -190,6 +190,13 @@ void SubViewportContainer::_propagate_nonpositional_event(const Ref<InputEvent> return; } + bool send; + if (GDVIRTUAL_CALL(_propagate_input_event, p_event, send)) { + if (!send) { + return; + } + } + _send_event_to_viewports(p_event); } @@ -204,6 +211,13 @@ void SubViewportContainer::gui_input(const Ref<InputEvent> &p_event) { return; } + bool send; + if (GDVIRTUAL_CALL(_propagate_input_event, p_event, send)) { + if (!send) { + return; + } + } + if (stretch && shrink > 1) { Transform2D xform; xform.scale(Vector2(1, 1) / shrink); @@ -275,6 +289,8 @@ void SubViewportContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stretch"), "set_stretch", "is_stretch_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_shrink"), "set_stretch_shrink", "get_stretch_shrink"); + + GDVIRTUAL_BIND(_propagate_input_event, "event"); } SubViewportContainer::SubViewportContainer() { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index 3c6cd09d66..06420de730 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -50,6 +50,8 @@ protected: virtual void add_child_notify(Node *p_child) override; virtual void remove_child_notify(Node *p_child) override; + GDVIRTUAL1RC(bool, _propagate_input_event, Ref<InputEvent>); + public: void set_stretch(bool p_enable); bool is_stretch_enabled() const; diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 9a915939c2..777ca96cc4 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -321,7 +321,6 @@ void TabBar::gui_input(const Ref<InputEvent> &p_event) { } void TabBar::_shape(int p_tab) { - tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); tabs.write[p_tab].text_buf->set_width(-1); if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { @@ -330,11 +329,17 @@ void TabBar::_shape(int p_tab) { tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction); } - tabs.write[p_tab].text_buf->add_string(tabs[p_tab].xl_text, theme_cache.font, theme_cache.font_size, tabs[p_tab].language); + tabs.write[p_tab].text_buf->add_string(atr(tabs[p_tab].text), theme_cache.font, theme_cache.font_size, tabs[p_tab].language); } void TabBar::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (scroll_to_selected) { + ensure_tab_visible(current); + } + } break; + case NOTIFICATION_INTERNAL_PROCESS: { Input *input = Input::get_singleton(); @@ -567,6 +572,8 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in rb->draw(ci, Point2i(rb_rect.position.x + style->get_margin(SIDE_LEFT), rb_rect.position.y + style->get_margin(SIDE_TOP))); p_x = rtl ? rb_rect.position.x : rb_rect.position.x + rb_rect.size.width; + } else { + tabs.write[p_index].rb_rect = Rect2(); } // Draw and calculate rect of the close button. @@ -668,7 +675,7 @@ bool TabBar::select_previous_available() { if (target_tab < 0) { target_tab += get_tab_count(); } - if (!is_tab_disabled(target_tab)) { + if (!is_tab_disabled(target_tab) && !is_tab_hidden(target_tab)) { set_current_tab(target_tab); return true; } @@ -680,7 +687,7 @@ bool TabBar::select_next_available() { const int offset_end = (get_tab_count() - get_current_tab()); for (int i = 1; i < offset_end; i++) { int target_tab = (get_current_tab() + i) % get_tab_count(); - if (!is_tab_disabled(target_tab)) { + if (!is_tab_disabled(target_tab) && !is_tab_hidden(target_tab)) { set_current_tab(target_tab); return true; } @@ -851,11 +858,6 @@ bool TabBar::is_tab_hidden(int p_tab) const { void TabBar::set_tab_metadata(int p_tab, const Variant &p_metadata) { ERR_FAIL_INDEX(p_tab, tabs.size()); - - if (tabs[p_tab].metadata == p_metadata) { - return; - } - tabs.write[p_tab].metadata = p_metadata; } @@ -1098,6 +1100,23 @@ void TabBar::remove_tab(int p_idx) { max_drawn_tab = 0; previous = 0; } else { + // Try to change to a valid tab if possible (without firing the `tab_selected` signal). + for (int i = current; i < tabs.size(); i++) { + if (!is_tab_disabled(i) && !is_tab_hidden(i)) { + current = i; + break; + } + } + // If nothing, try backwards. + if (is_tab_disabled(current) || is_tab_hidden(current)) { + for (int i = current - 1; i >= 0; i--) { + if (!is_tab_disabled(i) && !is_tab_hidden(i)) { + current = i; + break; + } + } + } + offset = MIN(offset, tabs.size() - 1); max_drawn_tab = MIN(max_drawn_tab, tabs.size() - 1); @@ -1121,7 +1140,26 @@ Variant TabBar::get_drag_data(const Point2 &p_point) { if (!drag_to_rearrange_enabled) { return Control::get_drag_data(p_point); // Allow stuff like TabContainer to override it. } + return _handle_get_drag_data("tab_bar_tab", p_point); +} + +bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { + if (!drag_to_rearrange_enabled) { + return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. + } + return _handle_can_drop_data("tab_bar_tab", p_point, p_data); +} + +void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { + if (!drag_to_rearrange_enabled) { + Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. + return; + } + _handle_drop_data("tab_bar_tab", p_point, p_data, callable_mp(this, &TabBar::move_tab), callable_mp(this, &TabBar::_move_tab_from)); +} + +Variant TabBar::_handle_get_drag_data(const String &p_type, const Point2 &p_point) { int tab_over = get_tab_idx_at_point(p_point); if (tab_over < 0) { return Variant(); @@ -1141,30 +1179,26 @@ Variant TabBar::get_drag_data(const Point2 &p_point) { drag_preview->add_child(tf); } - Label *label = memnew(Label(tabs[tab_over].xl_text)); + Label *label = memnew(Label(get_tab_title(tab_over))); drag_preview->add_child(label); set_drag_preview(drag_preview); Dictionary drag_data; - drag_data["type"] = "tab_element"; - drag_data["tab_element"] = tab_over; + drag_data["type"] = p_type; + drag_data["tab_index"] = tab_over; drag_data["from_path"] = get_path(); return drag_data; } -bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { - if (!drag_to_rearrange_enabled) { - return Control::can_drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. - } - +bool TabBar::_handle_can_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data) const { Dictionary d = p_data; if (!d.has("type")) { return false; } - if (String(d["type"]) == "tab_element") { + if (String(d["type"]) == p_type) { NodePath from_path = d["from_path"]; NodePath to_path = get_path(); if (from_path == to_path) { @@ -1182,19 +1216,14 @@ bool TabBar::can_drop_data(const Point2 &p_point, const Variant &p_data) const { return false; } -void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { - if (!drag_to_rearrange_enabled) { - Control::drop_data(p_point, p_data); // Allow stuff like TabContainer to override it. - return; - } - +void TabBar::_handle_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback) { Dictionary d = p_data; if (!d.has("type")) { return; } - if (String(d["type"]) == "tab_element") { - int tab_from_id = d["tab_element"]; + if (String(d["type"]) == p_type) { + int tab_from_id = d["tab_index"]; int hover_now = get_tab_idx_at_point(p_point); NodePath from_path = d["from_path"]; NodePath to_path = get_path(); @@ -1219,7 +1248,7 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now = is_layout_rtl() ^ (p_point.x < x) ? 0 : get_tab_count() - 1; } - move_tab(tab_from_id, hover_now); + p_move_tab_callback.call(tab_from_id, hover_now); if (!is_tab_disabled(hover_now)) { emit_signal(SNAME("active_tab_rearranged"), hover_now); set_current_tab(hover_now); @@ -1245,35 +1274,42 @@ void TabBar::drop_data(const Point2 &p_point, const Variant &p_data) { hover_now = tabs.is_empty() || (is_layout_rtl() ^ (p_point.x < get_tab_rect(0).position.x)) ? 0 : get_tab_count(); } - Tab moving_tab = from_tabs->tabs[tab_from_id]; - from_tabs->remove_tab(tab_from_id); - tabs.insert(hover_now, moving_tab); - - if (tabs.size() > 1) { - if (current >= hover_now) { - current++; - } - if (previous >= hover_now) { - previous++; - } - } + p_move_tab_from_other_callback.call(from_tabs, tab_from_id, hover_now); + } + } + } +} - if (!is_tab_disabled(hover_now)) { - set_current_tab(hover_now); - } else { - _update_cache(); - queue_redraw(); - } +void TabBar::_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) { + Tab moving_tab = p_from_tabbar->tabs[p_from_index]; + p_from_tabbar->remove_tab(p_from_index); + tabs.insert(p_to_index, moving_tab); - update_minimum_size(); + if (tabs.size() > 1) { + if (current >= p_to_index) { + current++; + } + if (previous >= p_to_index) { + previous++; + } + } - if (tabs.size() == 1) { - emit_signal(SNAME("tab_selected"), 0); - emit_signal(SNAME("tab_changed"), 0); - } - } + if (!is_tab_disabled(p_to_index)) { + set_current_tab(p_to_index); + if (tabs.size() == 1) { + _update_cache(); + queue_redraw(); + emit_signal(SNAME("tab_changed"), 0); + } + } else { + _update_cache(); + queue_redraw(); + if (tabs.size() == 1) { + emit_signal(SNAME("tab_changed"), 0); } } + + update_minimum_size(); } int TabBar::get_tab_idx_at_point(const Point2 &p_point) const { @@ -1732,7 +1768,10 @@ void TabBar::_bind_methods() { ADD_SIGNAL(MethodInfo("tab_hovered", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("active_tab_rearranged", PropertyInfo(Variant::INT, "idx_to"))); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); + // "current_tab" property must come after "tab_count", otherwise the property isn't loaded correctly. + ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_"); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_close_display_policy", PROPERTY_HINT_ENUM, "Show Never,Show Active Only,Show Always"), "set_tab_close_display_policy", "get_tab_close_display_policy"); @@ -1743,8 +1782,6 @@ void TabBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_to_selected"), "set_scroll_to_selected", "get_scroll_to_selected"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_with_rmb"), "set_select_with_rmb", "get_select_with_rmb"); - ADD_ARRAY_COUNT("Tabs", "tab_count", "set_tab_count", "get_tab_count", "tab_"); - BIND_ENUM_CONSTANT(ALIGNMENT_LEFT); BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); BIND_ENUM_CONSTANT(ALIGNMENT_RIGHT); diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 4bce30ea52..28e3411f3d 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -55,7 +55,6 @@ public: private: struct Tab { String text; - String xl_text; String language; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; @@ -167,8 +166,13 @@ protected: Variant get_drag_data(const Point2 &p_point) override; bool can_drop_data(const Point2 &p_point, const Variant &p_data) const override; void drop_data(const Point2 &p_point, const Variant &p_data) override; + void _move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index); public: + Variant _handle_get_drag_data(const String &p_type, const Point2 &p_point); + bool _handle_can_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data) const; + void _handle_drop_data(const String &p_type, const Point2 &p_point, const Variant &p_data, const Callable &p_move_tab_callback, const Callable &p_move_tab_from_other_callback); + void add_tab(const String &p_str = "", const Ref<Texture2D> &p_icon = Ref<Texture2D>()); void set_tab_title(int p_tab, const String &p_title); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index b757b516d1..aa9400847f 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -143,6 +143,13 @@ void TabContainer::_notification(int p_what) { } } break; + case NOTIFICATION_POST_ENTER_TREE: { + if (setup_current_tab >= 0) { + set_current_tab(setup_current_tab); + setup_current_tab = -1; + } + } break; + case NOTIFICATION_READY: case NOTIFICATION_RESIZED: { _update_margins(); @@ -191,6 +198,8 @@ void TabContainer::_on_theme_changed() { return; } + tab_bar->begin_bulk_theme_override(); + tab_bar->add_theme_style_override(SNAME("tab_unselected"), theme_cache.tab_unselected_style); tab_bar->add_theme_style_override(SNAME("tab_hovered"), theme_cache.tab_hovered_style); tab_bar->add_theme_style_override(SNAME("tab_selected"), theme_cache.tab_selected_style); @@ -217,6 +226,8 @@ void TabContainer::_on_theme_changed() { tab_bar->add_theme_constant_override(SNAME("icon_max_width"), theme_cache.icon_max_width); tab_bar->add_theme_constant_override(SNAME("outline_size"), theme_cache.outline_size); + tab_bar->end_bulk_theme_override(); + _update_margins(); if (get_tab_count() > 0) { _repaint(); @@ -329,140 +340,65 @@ Variant TabContainer::_get_drag_data_fw(const Point2 &p_point, Control *p_from_c if (!drag_to_rearrange_enabled) { return Variant(); } - - int tab_over = get_tab_idx_at_point(p_point); - if (tab_over < 0) { - return Variant(); - } - - HBoxContainer *drag_preview = memnew(HBoxContainer); - - Ref<Texture2D> icon = get_tab_icon(tab_over); - if (!icon.is_null()) { - TextureRect *tf = memnew(TextureRect); - tf->set_texture(icon); - drag_preview->add_child(tf); - } - - Label *label = memnew(Label(get_tab_title(tab_over))); - set_drag_preview(drag_preview); - drag_preview->add_child(label); - - Dictionary drag_data; - drag_data["type"] = "tabc_element"; - drag_data["tabc_element"] = tab_over; - drag_data["from_path"] = get_path(); - - return drag_data; + return tab_bar->_handle_get_drag_data("tab_container_tab", p_point); } bool TabContainer::_can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const { if (!drag_to_rearrange_enabled) { return false; } - - Dictionary d = p_data; - if (!d.has("type")) { - return false; - } - - if (String(d["type"]) == "tabc_element") { - NodePath from_path = d["from_path"]; - NodePath to_path = get_path(); - if (from_path == to_path) { - return true; - } else if (get_tabs_rearrange_group() != -1) { - // Drag and drop between other TabContainers. - Node *from_node = get_node(from_path); - TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); - if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { - return true; - } - } - } - - return false; + return tab_bar->_handle_can_drop_data("tab_container_tab", p_point, p_data); } void TabContainer::_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) { if (!drag_to_rearrange_enabled) { return; } + return tab_bar->_handle_drop_data("tab_container_tab", p_point, p_data, callable_mp(this, &TabContainer::_drag_move_tab), callable_mp(this, &TabContainer::_drag_move_tab_from)); +} + +void TabContainer::_drag_move_tab(int p_from_index, int p_to_index) { + move_child(get_tab_control(p_from_index), get_tab_control(p_to_index)->get_index(false)); +} - Dictionary d = p_data; - if (!d.has("type")) { +void TabContainer::_drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index) { + Node *parent = p_from_tabbar->get_parent(); + if (!parent) { return; } + TabContainer *from_tab_container = Object::cast_to<TabContainer>(parent); + if (!from_tab_container) { + return; + } + move_tab_from_tab_container(from_tab_container, p_from_index, p_to_index); +} - if (String(d["type"]) == "tabc_element") { - int tab_from_id = d["tabc_element"]; - int hover_now = get_tab_idx_at_point(p_point); - NodePath from_path = d["from_path"]; - NodePath to_path = get_path(); - - if (from_path == to_path) { - if (tab_from_id == hover_now) { - return; - } - - // Drop the new tab to the left or right depending on where the target tab is being hovered. - if (hover_now != -1) { - Rect2 tab_rect = tab_bar->get_tab_rect(hover_now); - if (is_layout_rtl() ^ (p_point.x <= tab_rect.position.x + tab_rect.size.width / 2)) { - if (hover_now > tab_from_id) { - hover_now -= 1; - } - } else if (tab_from_id > hover_now) { - hover_now += 1; - } - } else { - hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count() - 1; - } - - move_child(get_tab_control(tab_from_id), get_tab_control(hover_now)->get_index(false)); - if (!is_tab_disabled(hover_now)) { - emit_signal(SNAME("active_tab_rearranged"), hover_now); - set_current_tab(hover_now); - } - - } else if (get_tabs_rearrange_group() != -1) { - // Drag and drop between TabContainers. - - Node *from_node = get_node(from_path); - TabContainer *from_tabc = Object::cast_to<TabContainer>(from_node); - - if (from_tabc && from_tabc->get_tabs_rearrange_group() == get_tabs_rearrange_group()) { - // Get the tab properties before they get erased by the child removal. - String tab_title = from_tabc->get_tab_title(tab_from_id); - Ref<Texture2D> tab_icon = from_tabc->get_tab_icon(tab_from_id); - bool tab_disabled = from_tabc->is_tab_disabled(tab_from_id); - Variant tab_metadata = from_tabc->get_tab_metadata(tab_from_id); +void TabContainer::move_tab_from_tab_container(TabContainer *p_from, int p_from_index, int p_to_index) { + ERR_FAIL_NULL(p_from); + ERR_FAIL_INDEX(p_from_index, p_from->get_tab_count()); + ERR_FAIL_INDEX(p_to_index, get_tab_count() + 1); - // Drop the new tab to the left or right depending on where the target tab is being hovered. - if (hover_now != -1) { - Rect2 tab_rect = tab_bar->get_tab_rect(hover_now); - if (is_layout_rtl() ^ (p_point.x > tab_rect.position.x + tab_rect.size.width / 2)) { - hover_now += 1; - } - } else { - hover_now = is_layout_rtl() ^ (p_point.x < tab_bar->get_tab_rect(0).position.x) ? 0 : get_tab_count(); - } + // Get the tab properties before they get erased by the child removal. + String tab_title = p_from->get_tab_title(p_from_index); + Ref<Texture2D> tab_icon = p_from->get_tab_icon(p_from_index); + bool tab_disabled = p_from->is_tab_disabled(p_from_index); + Variant tab_metadata = p_from->get_tab_metadata(p_from_index); - Control *moving_tabc = from_tabc->get_tab_control(tab_from_id); - from_tabc->remove_child(moving_tabc); - add_child(moving_tabc, true); + Control *moving_tabc = p_from->get_tab_control(p_from_index); + p_from->remove_child(moving_tabc); + add_child(moving_tabc, true); - set_tab_title(get_tab_count() - 1, tab_title); - set_tab_icon(get_tab_count() - 1, tab_icon); - set_tab_disabled(get_tab_count() - 1, tab_disabled); - set_tab_metadata(get_tab_count() - 1, tab_metadata); + set_tab_title(get_tab_count() - 1, tab_title); + set_tab_icon(get_tab_count() - 1, tab_icon); + set_tab_disabled(get_tab_count() - 1, tab_disabled); + set_tab_metadata(get_tab_count() - 1, tab_metadata); - move_child(moving_tabc, get_tab_control(hover_now)->get_index(false)); - if (!is_tab_disabled(hover_now)) { - set_current_tab(hover_now); - } - } - } + if (p_to_index < 0 || p_to_index > get_tab_count() - 1) { + p_to_index = get_tab_count() - 1; + } + move_child(moving_tabc, get_tab_control(p_to_index)->get_index(false)); + if (!is_tab_disabled(p_to_index)) { + set_current_tab(p_to_index); } } @@ -492,6 +428,10 @@ void TabContainer::_on_tab_button_pressed(int p_tab) { emit_signal(SNAME("tab_button_pressed"), p_tab); } +void TabContainer::_on_active_tab_rearranged(int p_tab) { + emit_signal(SNAME("active_tab_rearranged"), p_tab); +} + void TabContainer::_refresh_tab_names() { Vector<Control *> controls = _get_tab_controls(); for (int i = 0; i < controls.size(); i++) { @@ -595,6 +535,10 @@ int TabContainer::get_tab_count() const { } void TabContainer::set_current_tab(int p_current) { + if (!is_inside_tree()) { + setup_current_tab = p_current; + return; + } tab_bar->set_current_tab(p_current); } @@ -979,7 +923,7 @@ void TabContainer::_bind_methods() { ADD_SIGNAL(MethodInfo("pre_popup_pressed")); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1", PROPERTY_USAGE_EDITOR), "set_current_tab", "get_current_tab"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front"); @@ -1034,6 +978,7 @@ TabContainer::TabContainer() { tab_bar->connect("tab_hovered", callable_mp(this, &TabContainer::_on_tab_hovered)); tab_bar->connect("tab_selected", callable_mp(this, &TabContainer::_on_tab_selected)); tab_bar->connect("tab_button_pressed", callable_mp(this, &TabContainer::_on_tab_button_pressed)); + tab_bar->connect("active_tab_rearranged", callable_mp(this, &TabContainer::_on_active_tab_rearranged)); connect("mouse_exited", callable_mp(this, &TabContainer::_on_mouse_exited)); } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index a831416612..450143cd0c 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -43,10 +43,11 @@ class TabContainer : public Container { bool all_tabs_in_front = false; bool menu_hovered = false; mutable ObjectID popup_obj_id; - bool drag_to_rearrange_enabled = false; bool use_hidden_tabs_for_min_size = false; bool theme_changing = false; Vector<Control *> children_removing; + bool drag_to_rearrange_enabled = false; + int setup_current_tab = -1; struct ThemeCache { int side_margin = 0; @@ -97,10 +98,13 @@ class TabContainer : public Container { void _on_tab_hovered(int p_tab); void _on_tab_selected(int p_tab); void _on_tab_button_pressed(int p_tab); + void _on_active_tab_rearranged(int p_tab); Variant _get_drag_data_fw(const Point2 &p_point, Control *p_from_control); bool _can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control) const; void _drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from_control); + void _drag_move_tab(int p_from_index, int p_to_index); + void _drag_move_tab_from(TabBar *p_from_tabbar, int p_from_index, int p_to_index); protected: virtual void gui_input(const Ref<InputEvent> &p_event) override; @@ -166,6 +170,8 @@ public: void set_popup(Node *p_popup); Popup *get_popup() const; + void move_tab_from_tab_container(TabContainer *p_from, int p_from_index, int p_to_index = -1); + void set_drag_to_rearrange_enabled(bool p_enabled); bool get_drag_to_rearrange_enabled() const; void set_tabs_rearrange_group(int p_group_id); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 9e159cd303..86e726d9da 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -212,9 +212,6 @@ void TextEdit::Text::invalidate_cache(int p_line, int p_column, bool p_text_chan for (int i = 0; i < spans; i++) { TS->shaped_set_span_update_font(r, i, font->get_rids(), font_size, font->get_opentype_features()); } - for (int i = 0; i < TextServer::SPACING_MAX; i++) { - TS->shaped_text_set_spacing(r, TextServer::SpacingType(i), font->get_spacing(TextServer::SpacingType(i))); - } } // Apply tab align. @@ -1072,12 +1069,7 @@ void TextEdit::_notification(int p_what) { if (rtl) { gutter_rect.position.x = size.width - gutter_rect.position.x - gutter_rect.size.x; } - - Variant args[3] = { line, g, Rect2(gutter_rect) }; - const Variant *argp[] = { &args[0], &args[1], &args[2] }; - Callable::CallError ce; - Variant ret; - gutter.custom_draw_callback.callp(argp, 3, ret, ce); + gutter.custom_draw_callback.call(line, g, Rect2(gutter_rect)); } } break; } @@ -1693,10 +1685,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { h_scroll->set_value(h_scroll->get_value() - (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { // Scroll 5 times as fast as normal (like in Visual Studio Code). - _scroll_up(15 * mb->get_factor()); + _scroll_up(15 * mb->get_factor(), true); } else if (v_scroll->is_visible()) { // Scroll 3 lines. - _scroll_up(3 * mb->get_factor()); + _scroll_up(3 * mb->get_factor(), true); } } if (mb->get_button_index() == MouseButton::WHEEL_DOWN && !mb->is_command_or_control_pressed()) { @@ -1704,10 +1696,10 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { h_scroll->set_value(h_scroll->get_value() + (100 * mb->get_factor())); } else if (mb->is_alt_pressed()) { // Scroll 5 times as fast as normal (like in Visual Studio Code). - _scroll_down(15 * mb->get_factor()); + _scroll_down(15 * mb->get_factor(), true); } else if (v_scroll->is_visible()) { // Scroll 3 lines. - _scroll_down(3 * mb->get_factor()); + _scroll_down(3 * mb->get_factor(), true); } } if (mb->get_button_index() == MouseButton::WHEEL_LEFT) { @@ -1941,9 +1933,9 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { if (pan_gesture.is_valid()) { const real_t delta = pan_gesture->get_delta().y; if (delta < 0) { - _scroll_up(-delta); + _scroll_up(-delta, false); } else { - _scroll_down(delta); + _scroll_down(delta, false); } h_scroll->set_value(h_scroll->get_value() + pan_gesture->get_delta().x * 100); if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { @@ -2956,6 +2948,8 @@ void TextEdit::_update_placeholder() { return; // Not in tree? } + const String placeholder_translated = atr(placeholder_text); + // 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()); @@ -2966,9 +2960,9 @@ void TextEdit::_update_placeholder() { placeholder_data_buf->set_direction((TextServer::Direction)text_direction); } placeholder_data_buf->set_preserve_control(draw_control_chars); - placeholder_data_buf->add_string(placeholder_text, theme_cache.font, theme_cache.font_size, language); + placeholder_data_buf->add_string(placeholder_translated, theme_cache.font, theme_cache.font_size, language); - placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_text); + placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_translated); if (placeholder_bidi_override.is_empty()) { TS->shaped_text_set_bidi_override(placeholder_data_buf->get_rid(), placeholder_bidi_override); } @@ -2993,7 +2987,7 @@ void TextEdit::_update_placeholder() { placeholder_wraped_rows.clear(); for (int i = 0; i <= wrap_amount; i++) { Vector2i line_range = placeholder_data_buf->get_line_range(i); - placeholder_wraped_rows.push_back(placeholder_text.substr(line_range.x, line_range.y - line_range.x)); + placeholder_wraped_rows.push_back(placeholder_translated.substr(line_range.x, line_range.y - line_range.x)); } } @@ -4112,6 +4106,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro int line = p_from_line; int pos = -1; + bool key_start_is_symbol = is_symbol(p_key[0]); + bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]); + for (int i = 0; i < text.size() + 1; i++) { if (line < 0) { line = text.size() - 1; @@ -4175,9 +4172,9 @@ Point2i TextEdit::search(const String &p_key, uint32_t p_search_flags, int p_fro if (pos != -1 && (p_search_flags & SEARCH_WHOLE_WORDS)) { // Validate for whole words. - if (pos > 0 && !is_symbol(text_line[pos - 1])) { + if (!key_start_is_symbol && pos > 0 && !is_symbol(text_line[pos - 1])) { is_match = false; - } else if (pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) { + } else if (!key_end_is_symbol && pos + p_key.length() < text_line.length() && !is_symbol(text_line[pos + p_key.length()])) { is_match = false; } } @@ -5473,7 +5470,7 @@ void TextEdit::set_line_as_last_visible(int p_line, int p_wrap_index) { set_v_scroll(0); return; } - set_v_scroll(get_scroll_pos_for_line(first_line, next_line.y) + _get_visible_lines_offset()); + set_v_scroll(Math::round(get_scroll_pos_for_line(first_line, next_line.y) + _get_visible_lines_offset())); } int TextEdit::get_last_full_visible_line() const { @@ -6433,14 +6430,6 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Arbitrary:1,Word:2,Word (Smart):3"), "set_autowrap_mode", "get_autowrap_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); - ADD_GROUP("Scroll", "scroll_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_smooth"), "set_smooth_scroll_enabled", "is_smooth_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scroll_v_scroll_speed", PROPERTY_HINT_NONE, "suffix:px/s"), "set_v_scroll_speed", "get_v_scroll_speed"); @@ -6462,6 +6451,16 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_mid_grapheme"), "set_caret_mid_grapheme_enabled", "is_caret_mid_grapheme_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_multiple"), "set_multiple_carets_enabled", "is_multiple_carets_enabled"); + ADD_GROUP("Highlighting", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "syntax_highlighter", PROPERTY_HINT_RESOURCE_TYPE, "SyntaxHighlighter", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_ALWAYS_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); + + ADD_GROUP("Visual Whitespace", "draw_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_tabs"), "set_draw_tabs", "is_drawing_tabs"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_spaces"), "set_draw_spaces", "is_drawing_spaces"); + ADD_GROUP("BiDi", ""); 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"); @@ -7020,6 +7019,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc p_from_column = 0; } + bool key_start_is_symbol = is_symbol(p_key[0]); + bool key_end_is_symbol = is_symbol(p_key[p_key.length() - 1]); + while (col == -1 && p_from_column <= p_search.length()) { if (p_search_flags & SEARCH_MATCH_CASE) { col = p_search.find(p_key, p_from_column); @@ -7036,9 +7038,9 @@ int TextEdit::_get_column_pos_of_word(const String &p_key, const String &p_searc if (col != -1 && p_search_flags & SEARCH_WHOLE_WORDS) { p_from_column = col; - if (col > 0 && !is_symbol(p_search[col - 1])) { + if (!key_start_is_symbol && col > 0 && !is_symbol(p_search[col - 1])) { col = -1; - } else if ((col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) { + } else if (!key_end_is_symbol && (col + p_key.length()) < p_search.length() && !is_symbol(p_search[col + p_key.length()])) { col = -1; } } @@ -7340,7 +7342,7 @@ void TextEdit::_update_scrollbars() { } int visible_width = size.width - theme_cache.style_normal->get_minimum_size().width; - int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + vmin.x + gutters_width + gutter_padding; + int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + gutters_width + gutter_padding; if (draw_minimap) { total_width += minimap_width; @@ -7357,11 +7359,6 @@ void TextEdit::_update_scrollbars() { v_scroll->show(); v_scroll->set_max(total_rows + _get_visible_lines_offset()); v_scroll->set_page(visible_rows + _get_visible_lines_offset()); - if (smooth_scroll_enabled) { - v_scroll->set_step(0.25); - } else { - v_scroll->set_step(1); - } set_v_scroll(get_v_scroll()); } else { @@ -7455,7 +7452,7 @@ double TextEdit::_get_v_scroll_offset() const { return CLAMP(val, 0, 1); } -void TextEdit::_scroll_up(real_t p_delta) { +void TextEdit::_scroll_up(real_t p_delta, bool p_animate) { if (scrolling && smooth_scroll_enabled && SIGN(target_v_scroll - v_scroll->get_value()) != SIGN(-p_delta)) { scrolling = false; minimap_clicked = false; @@ -7471,7 +7468,7 @@ void TextEdit::_scroll_up(real_t p_delta) { if (target_v_scroll <= 0) { target_v_scroll = 0; } - if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + if (!p_animate || Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { v_scroll->set_value(target_v_scroll); } else { scrolling = true; @@ -7482,7 +7479,7 @@ void TextEdit::_scroll_up(real_t p_delta) { } } -void TextEdit::_scroll_down(real_t p_delta) { +void TextEdit::_scroll_down(real_t p_delta, bool p_animate) { if (scrolling && smooth_scroll_enabled && SIGN(target_v_scroll - v_scroll->get_value()) != SIGN(p_delta)) { scrolling = false; minimap_clicked = false; @@ -7499,7 +7496,7 @@ void TextEdit::_scroll_down(real_t p_delta) { if (target_v_scroll > max_v_scroll) { target_v_scroll = max_v_scroll; } - if (Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { + if (!p_animate || Math::abs(target_v_scroll - v_scroll->get_value()) < 1.0) { v_scroll->set_value(target_v_scroll); } else { scrolling = true; @@ -7605,9 +7602,9 @@ void TextEdit::_update_minimap_click() { int first_line = row - next_line.x + 1; double delta = get_scroll_pos_for_line(first_line, next_line.y) - get_v_scroll(); if (delta < 0) { - _scroll_up(-delta); + _scroll_up(-delta, true); } else { - _scroll_down(delta); + _scroll_down(delta, true); } } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 56d5b67e0b..8a541b623b 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -500,8 +500,8 @@ private: double _get_visible_lines_offset() const; double _get_v_scroll_offset() const; - void _scroll_up(real_t p_delta); - void _scroll_down(real_t p_delta); + void _scroll_up(real_t p_delta, bool p_animate); + void _scroll_down(real_t p_delta, bool p_animate); void _scroll_lines_up(); void _scroll_lines_down(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index e2b16cdd66..a4c239cf53 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -1476,7 +1476,7 @@ Size2 TreeItem::get_minimum_size(int p_column) { void TreeItem::_call_recursive_bind(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (p_argcount < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_error.argument = 0; + r_error.expected = 1; return; } @@ -2050,7 +2050,7 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2 int text_width = item_width - theme_cache.inner_item_margin_left - theme_cache.inner_item_margin_right; if (p_item->cells[i].icon.is_valid()) { - text_width -= p_item->cells[i].get_icon_size().x + theme_cache.h_separation; + text_width -= _get_cell_icon_size(p_item->cells[i]).x + theme_cache.h_separation; } p_item->cells.write[i].text_buf->set_width(text_width); @@ -2770,21 +2770,9 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int x -= theme_cache.h_separation; } - if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { - if (enable_recursive_folding && p_mod->is_shift_pressed()) { - p_item->set_collapsed_recursive(!p_item->is_collapsed()); - } else { - p_item->set_collapsed(!p_item->is_collapsed()); - } - return -1; //collapse/uncollapse because nothing can be done with item - } - const TreeItem::Cell &c = p_item->cells[col]; - bool already_selected = c.selected; - bool already_cursor = (p_item == selected_item) && col == selected_col; - - if (!cache.rtl && p_item->cells[col].buttons.size()) { + if (!cache.rtl && !p_item->cells[col].buttons.is_empty()) { int button_w = 0; for (int j = p_item->cells[col].buttons.size() - 1; j >= 0; j--) { Ref<Texture2D> b = p_item->cells[col].buttons[j].texture; @@ -2827,6 +2815,18 @@ int Tree::propagate_mouse_event(const Point2i &p_pos, int x_ofs, int y_ofs, int col_width -= w + theme_cache.button_margin; } + if (!p_item->disable_folding && !hide_folding && !p_item->cells[col].editable && !p_item->cells[col].selectable && p_item->get_first_child()) { + if (enable_recursive_folding && p_mod->is_shift_pressed()) { + p_item->set_collapsed_recursive(!p_item->is_collapsed()); + } else { + p_item->set_collapsed(!p_item->is_collapsed()); + } + return -1; // Collapse/uncollapse, because nothing can be done with the item. + } + + bool already_selected = c.selected; + bool already_cursor = (p_item == selected_item) && col == selected_col; + if (p_button == MouseButton::LEFT || (p_button == MouseButton::RIGHT && allow_rmb_select)) { /* process selection */ @@ -3142,7 +3142,7 @@ void Tree::value_editor_changed(double p_value) { TreeItem::Cell &c = popup_edited_item->cells.write[popup_edited_item_col]; c.val = p_value; - text_editor->set_text(String::num(c.val, Math::range_step_decimals(c.step))); + line_editor->set_text(String::num(c.val, Math::range_step_decimals(c.step))); item_edited(popup_edited_item_col, popup_edited_item); queue_redraw(); diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index 6d1905f111..fc03f2d887 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -47,7 +47,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; - callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); + zoom_callback.call(zoom, mb->get_position(), p_event); return true; } else { Vector2 panning = scroll_vec * mb->get_factor(); @@ -58,7 +58,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) } else if (mb->is_shift_pressed()) { panning = Vector2(panning.y, panning.x); } - callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); + pan_callback.call(-panning * scroll_speed, p_event); return true; } } else { @@ -71,14 +71,14 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) } else if (mb->is_shift_pressed()) { panning = Vector2(panning.y, panning.x); } - callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); + pan_callback.call(-panning * scroll_speed, p_event); return true; } else if (!mb->is_shift_pressed()) { // Compute the zoom factor. float zoom_factor = mb->get_factor() <= 0 ? 1.0 : mb->get_factor(); zoom_factor = ((scroll_zoom_factor - 1.0) * zoom_factor) + 1.0; float zoom = (scroll_vec.x + scroll_vec.y) > 0 ? 1.0 / scroll_zoom_factor : scroll_zoom_factor; - callback_helper(zoom_callback, varray(zoom, mb->get_position(), p_event)); + zoom_callback.call(zoom, mb->get_position(), p_event); return true; } } @@ -108,9 +108,9 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (mm.is_valid()) { if (is_dragging) { if (p_canvas_rect != Rect2()) { - callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event)); + pan_callback.call(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event); } else { - callback_helper(pan_callback, varray(mm->get_relative(), p_event)); + pan_callback.call(mm->get_relative(), p_event); } return true; } @@ -119,13 +119,13 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) Ref<InputEventMagnifyGesture> magnify_gesture = p_event; if (magnify_gesture.is_valid()) { // Zoom gesture - callback_helper(zoom_callback, varray(magnify_gesture->get_factor(), magnify_gesture->get_position(), p_event)); + zoom_callback.call(magnify_gesture->get_factor(), magnify_gesture->get_position(), p_event); return true; } Ref<InputEventPanGesture> pan_gesture = p_event; if (pan_gesture.is_valid()) { - callback_helper(pan_callback, varray(-pan_gesture->get_delta() * scroll_speed, p_event)); + pan_callback.call(-pan_gesture->get_delta() * scroll_speed, p_event); } Ref<InputEventScreenDrag> screen_drag = p_event; @@ -134,7 +134,7 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) // This set of events also generates/is generated by // InputEventMouseButton/InputEventMouseMotion events which will be processed instead. } else { - callback_helper(pan_callback, varray(screen_drag->get_relative(), p_event)); + pan_callback.call(screen_drag->get_relative(), p_event); } } @@ -157,17 +157,6 @@ void ViewPanner::release_pan_key() { is_dragging = false; } -void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) { - const Variant **argptr = (const Variant **)alloca(sizeof(Variant *) * p_args.size()); - for (int i = 0; i < p_args.size(); i++) { - argptr[i] = &p_args[i]; - } - - Variant result; - Callable::CallError ce; - p_callback.callp(argptr, p_args.size(), result, ce); -} - void ViewPanner::set_callbacks(Callable p_pan_callback, Callable p_zoom_callback) { pan_callback = p_pan_callback; zoom_callback = p_zoom_callback; diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h index 60d36ca04c..5aec2d4f6b 100644 --- a/scene/gui/view_panner.h +++ b/scene/gui/view_panner.h @@ -68,7 +68,6 @@ private: Callable pan_callback; Callable zoom_callback; - void callback_helper(Callable p_callback, Vector<Variant> p_args); ControlScheme control_scheme = SCROLL_ZOOMS; public: |
