diff options
Diffstat (limited to 'scene/gui')
40 files changed, 765 insertions, 579 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 9cc25bf743..c26a00221a 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -30,6 +30,7 @@ #include "base_button.h" +#include "core/config/project_settings.h" #include "core/os/keyboard.h" #include "scene/main/window.h" #include "scene/scene_string_names.h" @@ -127,7 +128,6 @@ void BaseButton::_notification(int p_what) { status.hovering = false; status.press_attempt = false; status.pressing_inside = false; - status.shortcut_press = false; } break; } } @@ -154,14 +154,10 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { if (status.press_attempt && status.pressing_inside) { if (toggle_mode) { bool is_pressed = p_event->is_pressed(); - if (Object::cast_to<InputEventShortcut>(*p_event)) { - is_pressed = false; - } if ((is_pressed && action_mode == ACTION_MODE_BUTTON_PRESS) || (!is_pressed && action_mode == ACTION_MODE_BUTTON_RELEASE)) { if (action_mode == ACTION_MODE_BUTTON_PRESS) { status.press_attempt = false; status.pressing_inside = false; - status.shortcut_press = false; } status.pressed = !status.pressed; _unpress_group(); @@ -187,7 +183,6 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { } status.press_attempt = false; status.pressing_inside = false; - status.shortcut_press = false; emit_signal(SNAME("button_up")); } @@ -212,7 +207,6 @@ void BaseButton::set_disabled(bool p_disabled) { } status.press_attempt = false; status.pressing_inside = false; - status.shortcut_press = false; } queue_redraw(); } @@ -267,6 +261,10 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { return DRAW_DISABLED; } + if (in_shortcut_feedback) { + return DRAW_HOVER_PRESSED; + } + if (!status.press_attempt && status.hovering) { if (status.pressed) { return DRAW_HOVER_PRESSED; @@ -285,7 +283,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { pressing = status.pressed; } - if ((shortcut_feedback || !status.shortcut_press) && pressing) { + if (pressing) { return DRAW_PRESSED; } else { return DRAW_NORMAL; @@ -339,6 +337,14 @@ bool BaseButton::is_keep_pressed_outside() const { return keep_pressed_outside; } +void BaseButton::set_shortcut_feedback(bool p_enable) { + shortcut_feedback = p_enable; +} + +bool BaseButton::is_shortcut_feedback() const { + return shortcut_feedback; +} + void BaseButton::set_shortcut(const Ref<Shortcut> &p_shortcut) { shortcut = p_shortcut; set_process_shortcut_input(shortcut.is_valid()); @@ -348,13 +354,46 @@ Ref<Shortcut> BaseButton::get_shortcut() const { return shortcut; } +void BaseButton::_shortcut_feedback_timeout() { + in_shortcut_feedback = false; + queue_redraw(); +} + void BaseButton::shortcut_input(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); - if (!is_disabled() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) { - status.shortcut_press = true; - on_action_event(p_event); + if (!is_disabled() && p_event->is_pressed() && is_visible_in_tree() && !p_event->is_echo() && shortcut.is_valid() && shortcut->matches_event(p_event)) { + if (toggle_mode) { + status.pressed = !status.pressed; + + if (status.pressed) { + _unpress_group(); + if (button_group.is_valid()) { + button_group->emit_signal(SNAME("pressed"), this); + } + } + + _toggled(status.pressed); + _pressed(); + + } else { + _pressed(); + } + queue_redraw(); accept_event(); + + if (shortcut_feedback) { + if (shortcut_feedback_timer == nullptr) { + shortcut_feedback_timer = memnew(Timer); + shortcut_feedback_timer->set_one_shot(true); + add_child(shortcut_feedback_timer); + shortcut_feedback_timer->set_wait_time(GLOBAL_GET("gui/timers/button_shortcut_feedback_highlight_time")); + shortcut_feedback_timer->connect("timeout", callable_mp(this, &BaseButton::_shortcut_feedback_timeout)); + } + + in_shortcut_feedback = true; + shortcut_feedback_timer->start(); + } } } @@ -393,14 +432,6 @@ bool BaseButton::_was_pressed_by_mouse() const { return was_mouse_pressed; } -void BaseButton::set_shortcut_feedback(bool p_feedback) { - shortcut_feedback = p_feedback; -} - -bool BaseButton::is_shortcut_feedback() const { - return shortcut_feedback; -} - PackedStringArray BaseButton::get_configuration_warnings() const { PackedStringArray warnings = Control::get_configuration_warnings(); @@ -429,6 +460,8 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("get_draw_mode"), &BaseButton::get_draw_mode); ClassDB::bind_method(D_METHOD("set_keep_pressed_outside", "enabled"), &BaseButton::set_keep_pressed_outside); ClassDB::bind_method(D_METHOD("is_keep_pressed_outside"), &BaseButton::is_keep_pressed_outside); + ClassDB::bind_method(D_METHOD("set_shortcut_feedback", "enabled"), &BaseButton::set_shortcut_feedback); + ClassDB::bind_method(D_METHOD("is_shortcut_feedback"), &BaseButton::is_shortcut_feedback); ClassDB::bind_method(D_METHOD("set_shortcut", "shortcut"), &BaseButton::set_shortcut); ClassDB::bind_method(D_METHOD("get_shortcut"), &BaseButton::get_shortcut); @@ -436,9 +469,6 @@ void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_button_group", "button_group"), &BaseButton::set_button_group); ClassDB::bind_method(D_METHOD("get_button_group"), &BaseButton::get_button_group); - ClassDB::bind_method(D_METHOD("set_shortcut_feedback", "enabled"), &BaseButton::set_shortcut_feedback); - ClassDB::bind_method(D_METHOD("is_shortcut_feedback"), &BaseButton::is_shortcut_feedback); - GDVIRTUAL_BIND(_pressed); GDVIRTUAL_BIND(_toggled, "button_pressed"); @@ -449,14 +479,16 @@ void BaseButton::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toggle_mode"), "set_toggle_mode", "is_toggle_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "button_pressed"), "set_pressed", "is_pressed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "action_mode", PROPERTY_HINT_ENUM, "Button Press,Button Release"), "set_action_mode", "get_action_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "button_mask", PROPERTY_HINT_FLAGS, "Mouse Left, Mouse Right, Mouse Middle"), "set_button_mask", "get_button_mask"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_pressed_outside"), "set_keep_pressed_outside", "is_keep_pressed_outside"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + + ADD_GROUP("Shortcut", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shortcut", PROPERTY_HINT_RESOURCE_TYPE, "Shortcut"), "set_shortcut", "get_shortcut"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_feedback"), "set_shortcut_feedback", "is_shortcut_feedback"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "button_group", PROPERTY_HINT_RESOURCE_TYPE, "ButtonGroup"), "set_button_group", "get_button_group"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_in_tooltip"), "set_shortcut_in_tooltip", "is_shortcut_in_tooltip_enabled"); BIND_ENUM_CONSTANT(DRAW_NORMAL); BIND_ENUM_CONSTANT(DRAW_PRESSED); @@ -466,6 +498,8 @@ void BaseButton::_bind_methods() { BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_PRESS); BIND_ENUM_CONSTANT(ACTION_MODE_BUTTON_RELEASE); + + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/button_shortcut_feedback_highlight_time", PROPERTY_HINT_RANGE, "0.01,10,0.01,suffix:s"), 0.2); } BaseButton::BaseButton() { diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index f7c864c5fb..962a16c453 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -51,9 +51,9 @@ private: bool shortcut_in_tooltip = true; bool was_mouse_pressed = false; bool keep_pressed_outside = false; + bool shortcut_feedback = true; Ref<Shortcut> shortcut; ObjectID shortcut_context; - bool shortcut_feedback = true; ActionMode action_mode = ACTION_MODE_BUTTON_RELEASE; struct Status { @@ -61,7 +61,6 @@ private: bool hovering = false; bool press_attempt = false; bool pressing_inside = false; - bool shortcut_press = false; bool disabled = false; @@ -75,6 +74,10 @@ private: void on_action_event(Ref<InputEvent> p_event); + Timer *shortcut_feedback_timer = nullptr; + bool in_shortcut_feedback = false; + void _shortcut_feedback_timeout(); + protected: virtual void pressed(); virtual void toggled(bool p_pressed); @@ -122,6 +125,9 @@ public: void set_keep_pressed_outside(bool p_on); bool is_keep_pressed_outside() const; + void set_shortcut_feedback(bool p_enable); + bool is_shortcut_feedback() const; + void set_button_mask(BitField<MouseButtonMask> p_mask); BitField<MouseButtonMask> get_button_mask() const; @@ -133,9 +139,6 @@ public: void set_button_group(const Ref<ButtonGroup> &p_group); Ref<ButtonGroup> get_button_group() const; - void set_shortcut_feedback(bool p_feedback); - bool is_shortcut_feedback() const; - PackedStringArray get_configuration_warnings() const override; BaseButton(); diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 1e07a53642..2a8b1cd8c4 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -575,9 +575,13 @@ void Button::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Text Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); + + ADD_GROUP("Icon Behavior", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 7539810feb..b084cb5bea 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -548,7 +548,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (k->is_action("ui_text_dedent", true)) { - do_unindent(); + unindent_lines(); accept_event(); return; } @@ -898,50 +898,6 @@ void CodeEdit::indent_lines() { end_complex_operation(); } -void CodeEdit::do_unindent() { - if (!is_editable()) { - return; - } - - int cc = get_caret_column(); - - if (has_selection() || cc <= 0) { - unindent_lines(); - return; - } - - begin_complex_operation(); - Vector<int> caret_edit_order = get_caret_index_edit_order(); - for (const int &c : caret_edit_order) { - int cl = get_caret_line(c); - const String &line = get_line(cl); - - if (line[cc - 1] == '\t') { - remove_text(cl, cc - 1, cl, cc); - set_caret_column(MAX(0, cc - 1), c == 0, c); - adjust_carets_after_edit(c, cl, cc, cl, cc - 1); - continue; - } - - if (line[cc - 1] != ' ') { - continue; - } - - int spaces_to_remove = _calculate_spaces_till_next_left_indent(cc); - if (spaces_to_remove > 0) { - for (int i = 1; i <= spaces_to_remove; i++) { - if (line[cc - i] != ' ') { - spaces_to_remove = i - 1; - break; - } - } - remove_text(cl, cc - spaces_to_remove, cl, cc); - set_caret_column(MAX(0, cc - spaces_to_remove), c == 0, c); - } - } - end_complex_operation(); -} - void CodeEdit::unindent_lines() { if (!is_editable()) { return; @@ -2086,9 +2042,11 @@ void CodeEdit::confirm_code_completion(bool p_replace) { int post_brace_pair = get_caret_column(i) < get_line(caret_line).length() ? _get_auto_brace_pair_close_at_pos(caret_line, get_caret_column(i)) : -1; // Strings do not nest like brackets, so ensure we don't add an additional closing pair. - if (has_string_delimiter(String::chr(last_completion_char)) && post_brace_pair != -1 && last_char_matches) { - remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); - adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); + if (has_string_delimiter(String::chr(last_completion_char))) { + if (post_brace_pair != -1 && last_char_matches) { + remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); + adjust_carets_after_edit(i, caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); + } } else { if (pre_brace_pair != -1 && pre_brace_pair != post_brace_pair && last_char_matches) { remove_text(caret_line, get_caret_column(i), caret_line, get_caret_column(i) + 1); @@ -2202,7 +2160,6 @@ void CodeEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_auto_indent_prefixes"), &CodeEdit::get_auto_indent_prefixes); ClassDB::bind_method(D_METHOD("do_indent"), &CodeEdit::do_indent); - ClassDB::bind_method(D_METHOD("do_unindent"), &CodeEdit::do_unindent); ClassDB::bind_method(D_METHOD("indent_lines"), &CodeEdit::indent_lines); ClassDB::bind_method(D_METHOD("unindent_lines"), &CodeEdit::unindent_lines); @@ -3131,6 +3088,8 @@ void CodeEdit::_filter_code_completion_candidates_impl() { } code_completion_options.append_array(completion_options_casei); + code_completion_options.append_array(completion_options_substr); + code_completion_options.append_array(completion_options_substr_casei); code_completion_options.append_array(completion_options_subseq); code_completion_options.append_array(completion_options_subseq_casei); diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index c050b2266f..55fc5aa2ae 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -289,7 +289,6 @@ public: TypedArray<String> get_auto_indent_prefixes() const; void do_indent(); - void do_unindent(); void indent_lines(); void unindent_lines(); diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index b5846cb692..da29bc823f 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -35,11 +35,6 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "scene/gui/color_mode.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - #include "thirdparty/misc/ok_color.h" #include "thirdparty/misc/ok_color_shader.h" @@ -50,31 +45,6 @@ void ColorPicker::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { _update_color(); -#ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { - if (preset_cache.is_empty()) { - PackedColorArray saved_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "presets", PackedColorArray()); - for (int i = 0; i < saved_presets.size(); i++) { - preset_cache.push_back(saved_presets[i]); - } - } - - for (int i = 0; i < preset_cache.size(); i++) { - presets.push_back(preset_cache[i]); - } - - if (recent_preset_cache.is_empty()) { - PackedColorArray saved_recent_presets = EditorSettings::get_singleton()->get_project_metadata("color_picker", "recent_presets", PackedColorArray()); - for (int i = 0; i < saved_recent_presets.size(); i++) { - recent_preset_cache.push_back(saved_recent_presets[i]); - } - } - - for (int i = 0; i < recent_preset_cache.size(); i++) { - recent_presets.push_back(recent_preset_cache[i]); - } - } -#endif [[fallthrough]]; } case NOTIFICATION_THEME_CHANGED: { @@ -404,6 +374,40 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) { } } +#ifdef TOOLS_ENABLED +void ColorPicker::set_editor_settings(Object *p_editor_settings) { + if (editor_settings) { + return; + } + editor_settings = p_editor_settings; + + if (preset_cache.is_empty()) { + PackedColorArray saved_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "presets", PackedColorArray()); + for (int i = 0; i < saved_presets.size(); i++) { + preset_cache.push_back(saved_presets[i]); + } + } + + for (int i = 0; i < preset_cache.size(); i++) { + presets.push_back(preset_cache[i]); + } + + if (recent_preset_cache.is_empty()) { + PackedColorArray saved_recent_presets = editor_settings->call(SNAME("get_project_metadata"), "color_picker", "recent_presets", PackedColorArray()); + for (int i = 0; i < saved_recent_presets.size(); i++) { + recent_preset_cache.push_back(saved_recent_presets[i]); + } + } + + for (int i = 0; i < recent_preset_cache.size(); i++) { + recent_presets.push_back(recent_preset_cache[i]); + } + + _update_presets(); + _update_recent_presets(); +} +#endif + HSlider *ColorPicker::get_slider(int p_idx) { if (p_idx < SLIDER_COUNT) { return sliders[p_idx]; @@ -471,7 +475,7 @@ ColorPicker::PickerShapeType ColorPicker::_get_actual_shape() const { void ColorPicker::_reset_theme() { Ref<StyleBoxFlat> style_box_flat(memnew(StyleBoxFlat)); - style_box_flat->set_default_margin(SIDE_TOP, 16 * get_theme_default_base_scale()); + style_box_flat->set_content_margin(SIDE_TOP, 16 * get_theme_default_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]->add_theme_icon_override("grabber", get_theme_icon(SNAME("bar_arrow"), SNAME("ColorPicker"))); @@ -553,7 +557,7 @@ void ColorPicker::_update_presets() { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { // Only load preset buttons when the only child is the add-preset button. if (preset_container->get_child_count() == 1) { for (int i = 0; i < preset_cache.size(); i++) { @@ -567,7 +571,7 @@ void ColorPicker::_update_presets() { void ColorPicker::_update_recent_presets() { #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { int recent_preset_count = recent_preset_hbc->get_child_count(); for (int i = 0; i < recent_preset_count; i++) { memdelete(recent_preset_hbc->get_child(0)); @@ -642,7 +646,7 @@ inline int ColorPicker::_get_preset_size() { void ColorPicker::_add_preset_button(int p_size, const Color &p_color) { ColorPresetButton *btn_preset_new = memnew(ColorPresetButton(p_color, p_size)); btn_preset_new->set_tooltip_text(vformat(RTR("Color: #%s\nLMB: Apply color\nRMB: Remove preset"), p_color.to_html(p_color.a < 1))); - btn_preset_new->set_drag_forwarding_compat(this); + SET_DRAG_FORWARDING_GCDU(btn_preset_new, ColorPicker); btn_preset_new->set_button_group(preset_group); preset_container->add_child(btn_preset_new); btn_preset_new->set_pressed(true); @@ -743,9 +747,9 @@ void ColorPicker::add_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save); } #endif } @@ -764,9 +768,9 @@ void ColorPicker::add_recent_preset(const Color &p_color) { _select_from_preset_container(p_color); #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_recent_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "recent_presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save); } #endif } @@ -787,9 +791,9 @@ void ColorPicker::erase_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "presets", arr_to_save); } #endif } @@ -811,9 +815,9 @@ void ColorPicker::erase_recent_preset(const Color &p_color) { } #ifdef TOOLS_ENABLED - if (Engine::get_singleton()->is_editor_hint()) { + if (editor_settings) { PackedColorArray arr_to_save = get_recent_presets(); - EditorSettings::get_singleton()->set_project_metadata("color_picker", "recent_presets", arr_to_save); + editor_settings->call(SNAME("set_project_metadata"), "color_picker", "recent_presets", arr_to_save); } #endif } @@ -890,7 +894,7 @@ void ColorPicker::set_colorize_sliders(bool p_colorize_sliders) { alpha_slider->add_theme_style_override("slider", style_box_empty); } else { Ref<StyleBoxFlat> style_box_flat(memnew(StyleBoxFlat)); - style_box_flat->set_default_margin(SIDE_TOP, 16 * get_theme_default_base_scale()); + style_box_flat->set_content_margin(SIDE_TOP, 16 * get_theme_default_base_scale()); style_box_flat->set_bg_color(Color(0.2, 0.23, 0.31).lerp(Color(0, 0, 0, 1), 0.3).clamp()); if (!slider_theme_modified) { @@ -1544,10 +1548,6 @@ void ColorPicker::_bind_methods() { ClassDB::bind_method(D_METHOD("set_picker_shape", "shape"), &ColorPicker::set_picker_shape); ClassDB::bind_method(D_METHOD("get_picker_shape"), &ColorPicker::get_picker_shape); - ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &ColorPicker::_get_drag_data_fw); - ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &ColorPicker::_can_drop_data_fw); - ClassDB::bind_method(D_METHOD("_drop_data_fw"), &ColorPicker::_drop_data_fw); - ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_pick_color", "get_pick_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "edit_alpha"), "set_edit_alpha", "is_editing_alpha"); ADD_PROPERTY(PropertyInfo(Variant::INT, "color_mode", PROPERTY_HINT_ENUM, "RGB,HSV,RAW,OKHSL"), "set_color_mode", "get_color_mode"); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 5eaeecca2a..f7578612cd 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -100,6 +100,10 @@ private: static List<Color> preset_cache; static List<Color> recent_preset_cache; +#ifdef TOOLS_ENABLED + Object *editor_settings = nullptr; +#endif + int current_slider_count = SLIDER_COUNT; static const int MODE_BUTTON_COUNT = 3; @@ -231,6 +235,10 @@ protected: static void _bind_methods(); public: +#ifdef TOOLS_ENABLED + void set_editor_settings(Object *p_editor_settings); +#endif + HSlider *get_slider(int idx); Vector<float> get_active_slider_values(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 4188946494..6f5e2cf058 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -692,6 +692,12 @@ Transform2D Control::get_transform() const { return xform; } +void Control::_toplevel_changed_on_parent() { + // Update root control status. + _notification(NOTIFICATION_EXIT_CANVAS); + _notification(NOTIFICATION_ENTER_CANVAS); +} + /// Anchors and offsets. void Control::_set_anchor(Side p_side, real_t p_anchor) { @@ -758,6 +764,7 @@ void Control::set_anchor_and_offset(Side p_side, real_t p_anchor, real_t p_pos, } void Control::set_begin(const Size2 &p_point) { + ERR_FAIL_COND(!isfinite(p_point.x) || !isfinite(p_point.y)); if (data.offset[0] == p_point.x && data.offset[1] == p_point.y) { return; } @@ -1386,13 +1393,7 @@ Point2 Control::get_global_position() const { Point2 Control::get_screen_position() const { ERR_FAIL_COND_V(!is_inside_tree(), Point2()); - Point2 global_pos = get_global_transform_with_canvas().get_origin(); - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - global_pos += w->get_position(); - } - - return global_pos; + return get_screen_transform().get_origin(); } void Control::_set_size(const Size2 &p_size) { @@ -1405,6 +1406,7 @@ void Control::_set_size(const Size2 &p_size) { } void Control::set_size(const Size2 &p_size, bool p_keep_offsets) { + ERR_FAIL_COND(!isfinite(p_size.x) || !isfinite(p_size.y)); Size2 new_size = p_size; Size2 min = get_combined_minimum_size(); if (new_size.x < min.x) { @@ -1442,24 +1444,20 @@ void Control::set_rect(const Rect2 &p_rect) { } Rect2 Control::get_rect() const { - return Rect2(get_position(), get_size()); + Transform2D xform = get_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_global_rect() const { - return Rect2(get_global_position(), get_size()); + Transform2D xform = get_global_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_screen_rect() const { ERR_FAIL_COND_V(!is_inside_tree(), Rect2()); - Rect2 r(get_global_position(), get_size()); - - Window *w = Object::cast_to<Window>(get_viewport()); - if (w && !w->is_embedding_subwindows()) { - r.position += w->get_position(); - } - - return r; + Transform2D xform = get_screen_transform(); + return Rect2(xform.get_origin(), xform.get_scale() * get_size()); } Rect2 Control::get_anchorable_rect() const { @@ -1580,10 +1578,6 @@ void Control::set_block_minimum_size_adjust(bool p_block) { data.block_minimum_size_adjust = p_block; } -bool Control::is_minimum_size_adjust_blocked() const { - return data.block_minimum_size_adjust; -} - Size2 Control::get_minimum_size() const { Vector2 ms; GDVIRTUAL_CALL(_get_minimum_size, ms); @@ -1595,7 +1589,7 @@ void Control::set_custom_minimum_size(const Size2 &p_custom) { return; } - if (isnan(p_custom.x) || isnan(p_custom.y)) { + if (!isfinite(p_custom.x) || !isfinite(p_custom.y)) { // Prevent infinite loop. return; } @@ -1821,19 +1815,6 @@ bool Control::is_focus_owner_in_shortcut_context() const { // Drag and drop handling. -void Control::set_drag_forwarding_compat(Object *p_base) { - if (p_base != nullptr) { - data.forward_drag = Callable(p_base, "_get_drag_data_fw").bind(this); - data.forward_can_drop = Callable(p_base, "_can_drop_data_fw").bind(this); - data.forward_drop = Callable(p_base, "_drop_data_fw").bind(this); - - } else { - data.forward_drag = Callable(); - data.forward_can_drop = Callable(); - data.forward_drop = Callable(); - } -} - void Control::set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop) { data.forward_drag = p_drag; data.forward_can_drop = p_can_drop; @@ -2780,9 +2761,9 @@ void Control::end_bulk_theme_override() { // Internationalization. -TypedArray<Vector2i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { +TypedArray<Vector3i> Control::structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const { if (p_parser_type == TextServer::STRUCTURED_TEXT_CUSTOM) { - TypedArray<Vector2i> ret; + TypedArray<Vector3i> ret; GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret); return ret; } else { diff --git a/scene/gui/control.h b/scene/gui/control.h index 22a37dd89e..5977f4dbea 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -145,7 +145,7 @@ public: TEXT_DIRECTION_AUTO = TextServer::DIRECTION_AUTO, TEXT_DIRECTION_LTR = TextServer::DIRECTION_LTR, TEXT_DIRECTION_RTL = TextServer::DIRECTION_RTL, - TEXT_DIRECTION_INHERITED, + TEXT_DIRECTION_INHERITED = TextServer::DIRECTION_INHERITED, }; private: @@ -292,6 +292,9 @@ private: void _update_minimum_size(); void _size_changed(); + void _toplevel_changed() override{}; // Controls don't need to do anything, only other CanvasItems. + void _toplevel_changed_on_parent() override; + void _clear_size_warning(); // Input events. @@ -330,7 +333,7 @@ protected: // Internationalization. - virtual TypedArray<Vector2i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; + virtual TypedArray<Vector3i> structured_text_parser(TextServer::StructuredTextParser p_parser_type, const Array &p_args, const String &p_text) const; // Base object overrides. @@ -340,7 +343,7 @@ protected: // Exposed virtual methods. GDVIRTUAL1RC(bool, _has_point, Vector2) - GDVIRTUAL2RC(TypedArray<Vector2i>, _structured_text_parser, Array, String) + GDVIRTUAL2RC(TypedArray<Vector3i>, _structured_text_parser, Array, String) GDVIRTUAL0RC(Vector2, _get_minimum_size) GDVIRTUAL1RC(Variant, _get_drag_data, Vector2) @@ -464,7 +467,6 @@ public: void update_minimum_size(); void set_block_minimum_size_adjust(bool p_block); - bool is_minimum_size_adjust_blocked() const; virtual Size2 get_minimum_size() const; virtual Size2 get_combined_minimum_size() const; @@ -503,7 +505,6 @@ public: // Drag and drop handling. virtual void set_drag_forwarding(const Callable &p_drag, const Callable &p_can_drop, const Callable &p_drop); - virtual void set_drag_forwarding_compat(Object *p_base); virtual Variant get_drag_data(const Point2 &p_point); virtual bool can_drop_data(const Point2 &p_point, const Variant &p_data) const; virtual void drop_data(const Point2 &p_point, const Variant &p_data); @@ -634,4 +635,10 @@ VARIANT_ENUM_CAST(Control::LayoutMode); VARIANT_ENUM_CAST(Control::LayoutDirection); VARIANT_ENUM_CAST(Control::TextDirection); +// G = get_drag_data_fw, C = can_drop_data_fw, D = drop_data_fw, U = underscore +#define SET_DRAG_FORWARDING_CD(from, to) from->set_drag_forwarding(Callable(), callable_mp(this, &to::can_drop_data_fw).bind(from), callable_mp(this, &to::drop_data_fw).bind(from)); +#define SET_DRAG_FORWARDING_CDU(from, to) from->set_drag_forwarding(Callable(), callable_mp(this, &to::_can_drop_data_fw).bind(from), callable_mp(this, &to::_drop_data_fw).bind(from)); +#define SET_DRAG_FORWARDING_GCD(from, to) from->set_drag_forwarding(callable_mp(this, &to::get_drag_data_fw).bind(from), callable_mp(this, &to::can_drop_data_fw).bind(from), callable_mp(this, &to::drop_data_fw).bind(from)); +#define SET_DRAG_FORWARDING_GCDU(from, to) from->set_drag_forwarding(callable_mp(this, &to::_get_drag_data_fw).bind(from), callable_mp(this, &to::_can_drop_data_fw).bind(from), callable_mp(this, &to::_drop_data_fw).bind(from)); + #endif // CONTROL_H diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index 4365db2ea2..dfe9ea3b08 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -136,7 +136,7 @@ void AcceptDialog::_cancel_pressed() { call_deferred(SNAME("hide")); - emit_signal(SNAME("cancelled")); + emit_signal(SNAME("canceled")); cancel_pressed(); @@ -372,7 +372,7 @@ void AcceptDialog::_bind_methods() { ClassDB::bind_method(D_METHOD("get_ok_button_text"), &AcceptDialog::get_ok_button_text); ADD_SIGNAL(MethodInfo("confirmed")); - ADD_SIGNAL(MethodInfo("cancelled")); + ADD_SIGNAL(MethodInfo("canceled")); ADD_SIGNAL(MethodInfo("custom_action", PropertyInfo(Variant::STRING_NAME, "action"))); ADD_PROPERTY(PropertyInfo(Variant::STRING, "ok_button_text"), "set_ok_button_text", "get_ok_button_text"); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 2fd6d666e5..dc23bcb14a 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -920,7 +920,8 @@ void GraphEdit::_draw_connection_line(CanvasItem *p_where, const Vector2 &p_from scaled_points.push_back(points[i] * p_zoom); } - p_where->draw_polyline_colors(scaled_points, colors, Math::floor(p_width * get_theme_default_base_scale()), lines_antialiased); + // Thickness below 0.5 doesn't look good on the graph or its minimap. + p_where->draw_polyline_colors(scaled_points, colors, MAX(0.5, Math::floor(p_width * get_theme_default_base_scale())), lines_antialiased); } void GraphEdit::_connections_layer_draw() { @@ -1088,7 +1089,7 @@ void GraphEdit::_minimap_draw() { from_color = from_color.lerp(activity_color, E.activity); to_color = to_color.lerp(activity_color, E.activity); } - _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.1, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length()); + _draw_connection_line(minimap, from_position, to_position, from_color, to_color, 0.5, minimap->_convert_from_graph_position(Vector2(zoom, zoom)).length()); } // Draw the "camera" viewport. @@ -1380,34 +1381,15 @@ void GraphEdit::gui_input(const Ref<InputEvent> &p_ev) { accept_event(); } } - - Ref<InputEventMagnifyGesture> magnify_gesture = p_ev; - if (magnify_gesture.is_valid()) { - set_zoom_custom(zoom * magnify_gesture->get_factor(), magnify_gesture->get_position()); - } - - Ref<InputEventPanGesture> pan_gesture = p_ev; - if (pan_gesture.is_valid()) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); - } -} - -void GraphEdit::_scroll_callback(Vector2 p_scroll_vec, bool p_alt) { - if (p_scroll_vec.x != 0) { - h_scroll->set_value(h_scroll->get_value() + (h_scroll->get_page() * Math::abs(p_scroll_vec.x) / 8) * SIGN(p_scroll_vec.x)); - } else { - v_scroll->set_value(v_scroll->get_value() + (v_scroll->get_page() * Math::abs(p_scroll_vec.y) / 8) * SIGN(p_scroll_vec.y)); - } } -void GraphEdit::_pan_callback(Vector2 p_scroll_vec) { +void GraphEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) { h_scroll->set_value(h_scroll->get_value() - p_scroll_vec.x); v_scroll->set_value(v_scroll->get_value() - p_scroll_vec.y); } -void GraphEdit::_zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt) { - set_zoom_custom(p_scroll_vec.y < 0 ? zoom * zoom_step : zoom / zoom_step, p_origin); +void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { + set_zoom_custom(zoom * p_zoom_factor, p_origin); } void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) { @@ -1496,11 +1478,13 @@ float GraphEdit::get_zoom() const { void GraphEdit::set_zoom_step(float p_zoom_step) { p_zoom_step = abs(p_zoom_step); + ERR_FAIL_COND(!isfinite(p_zoom_step)); if (zoom_step == p_zoom_step) { return; } zoom_step = p_zoom_step; + panner->set_scroll_zoom_factor(zoom_step); } float GraphEdit::get_zoom_step() const { @@ -2420,7 +2404,7 @@ GraphEdit::GraphEdit() { zoom_max = (1 * Math::pow(zoom_step, 4)); panner.instantiate(); - panner->set_callbacks(callable_mp(this, &GraphEdit::_scroll_callback), callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); + panner->set_callbacks(callable_mp(this, &GraphEdit::_pan_callback), callable_mp(this, &GraphEdit::_zoom_callback)); top_layer = memnew(GraphEditFilter(this)); add_child(top_layer, false, INTERNAL_MODE_BACK); diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 030f40e370..dfe6b94906 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -129,9 +129,8 @@ private: Ref<ViewPanner> panner; bool warped_panning = true; - void _scroll_callback(Vector2 p_scroll_vec, bool p_alt); - void _pan_callback(Vector2 p_scroll_vec); - void _zoom_callback(Vector2 p_scroll_vec, Vector2 p_origin, bool p_alt); + void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); + void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); bool arrange_nodes_button_hidden = false; diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 372baeadae..25a27d5e1a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -309,12 +309,6 @@ void ItemList::set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon) { shape_changed = true; } -Ref<Texture2D> ItemList::get_item_tag_icon(int p_idx) const { - ERR_FAIL_INDEX_V(p_idx, items.size(), Ref<Texture2D>()); - - return items[p_idx].tag_icon; -} - void ItemList::set_item_selectable(int p_idx, bool p_selectable) { if (p_idx < 0) { p_idx += get_item_count(); diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h index 848f9a2ba9..934318dbb4 100644 --- a/scene/gui/item_list.h +++ b/scene/gui/item_list.h @@ -191,7 +191,6 @@ public: Variant get_item_metadata(int p_idx) const; void set_item_tag_icon(int p_idx, const Ref<Texture2D> &p_tag_icon); - Ref<Texture2D> get_item_tag_icon(int p_idx) const; void set_item_tooltip_enabled(int p_idx, const bool p_enabled); bool is_item_tooltip_enabled(int p_idx) const; diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index cafea83f16..f59702835c 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -222,6 +222,7 @@ void Label::_shape() { } } lines_dirty = false; + lines_shaped_last_width = get_size().width; } _update_visible(); @@ -596,7 +597,13 @@ void Label::_notification(int p_what) { } break; case NOTIFICATION_RESIZED: { - lines_dirty = true; + // It may happen that the reshaping due to this size change triggers a cascade of re-layout + // across the hierarchy where this label belongs to in a way that its size changes multiple + // times, but ending up with the original size it was already shaped for. + // This check prevents the catastrophic, freezing infinite cascade of re-layout. + if (lines_shaped_last_width != get_size().width) { + lines_dirty = true; + } } break; } } diff --git a/scene/gui/label.h b/scene/gui/label.h index 2350525236..b80646810b 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -49,6 +49,8 @@ private: bool uppercase = false; bool lines_dirty = true; + int lines_shaped_last_width = -1; + bool dirty = true; bool font_dirty = true; RID text_rid; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index e5bb64b225..dba08e16cb 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -247,7 +247,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { return; } if (b->is_pressed() && b->get_button_index() == MouseButton::RIGHT && context_menu_enabled) { - _ensure_menu(); + _update_context_menu(); menu->set_position(get_screen_position() + get_local_mouse_position()); menu->reset_size(); menu->popup(); @@ -452,7 +452,7 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { if (context_menu_enabled) { if (k->is_action("ui_menu", true)) { - _ensure_menu(); + _update_context_menu(); Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); menu->set_position(get_screen_position() + pos); menu->reset_size(); @@ -952,9 +952,9 @@ void LineEdit::_notification(int p_what) { // Prevent carets from disappearing at theme scales below 1.0 (if the caret width is 1). const int caret_width = theme_cache.caret_width * MAX(1, theme_cache.base_scale); - if (ime_text.length() == 0) { + if (ime_text.is_empty() || ime_selection.y == 0) { // Normal caret. - CaretInfo caret = TS->shaped_text_get_carets(text_rid, caret_column); + CaretInfo caret = TS->shaped_text_get_carets(text_rid, ime_text.is_empty() ? caret_column : caret_column + ime_selection.x); if (using_placeholder || (caret.l_caret == Rect2() && caret.t_caret == Rect2())) { // No carets, add one at the start. int h = theme_cache.font->get_height(theme_cache.font_size); @@ -980,6 +980,7 @@ void LineEdit::_notification(int p_what) { } } break; } + RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.l_caret, caret_color); } else { if (caret.l_caret != Rect2() && caret.l_dir == TextServer::DIRECTION_AUTO) { @@ -1009,7 +1010,8 @@ void LineEdit::_notification(int p_what) { RenderingServer::get_singleton()->canvas_item_add_rect(ci, caret.t_caret, caret_color); } - } else { + } + if (!ime_text.is_empty()) { { // IME intermediate text range. Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column, caret_column + ime_text.length()); @@ -1030,20 +1032,22 @@ void LineEdit::_notification(int p_what) { } { // IME caret. - Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y); - for (int i = 0; i < sel.size(); i++) { - Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); - if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { - continue; - } - if (rect.position.x < x_ofs) { - rect.size.x -= (x_ofs - rect.position.x); - rect.position.x = x_ofs; - } else if (rect.position.x + rect.size.x > ofs_max) { - rect.size.x = ofs_max - rect.position.x; + if (ime_selection.y > 0) { + Vector<Vector2> sel = TS->shaped_text_get_selection(text_rid, caret_column + ime_selection.x, caret_column + ime_selection.x + ime_selection.y); + for (int i = 0; i < sel.size(); i++) { + Rect2 rect = Rect2(sel[i].x + ofs.x, ofs.y, sel[i].y - sel[i].x, text_height); + if (rect.position.x + rect.size.x <= x_ofs || rect.position.x > ofs_max) { + continue; + } + if (rect.position.x < x_ofs) { + rect.size.x -= (x_ofs - rect.position.x); + rect.position.x = x_ofs; + } else if (rect.position.x + rect.size.x > ofs_max) { + rect.size.x = ofs_max - rect.position.x; + } + rect.size.y = caret_width * 3; + RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } - rect.size.y = caret_width * 3; - RenderingServer::get_singleton()->canvas_item_add_rect(ci, rect, caret_color); } } } @@ -1052,7 +1056,8 @@ void LineEdit::_notification(int p_what) { if (has_focus()) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + Point2(using_placeholder ? 0 : x_ofs, y_ofs + TS->shaped_text_get_size(text_rid).y), get_viewport()->get_window_id()); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + pos, get_viewport()->get_window_id()); } } } break; @@ -1071,8 +1076,8 @@ void LineEdit::_notification(int p_what) { if (get_viewport()->get_window_id() != DisplayServer::INVALID_WINDOW_ID && DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_IME)) { DisplayServer::get_singleton()->window_set_ime_active(true, get_viewport()->get_window_id()); - Point2 column = Point2(get_caret_column(), 1) * get_minimum_size().height; - DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + column, get_viewport()->get_window_id()); + Point2 pos = Point2(get_caret_pixel_pos().x, (get_size().y + theme_cache.font->get_height(theme_cache.font_size)) / 2); + DisplayServer::get_singleton()->window_set_ime_position(get_global_position() + pos, get_viewport()->get_window_id()); } show_virtual_keyboard(); @@ -1103,6 +1108,11 @@ void LineEdit::_notification(int p_what) { if (has_focus()) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); + + if (!ime_text.is_empty()) { + selection_delete(); + } + _shape(); set_caret_column(caret_column); // Update scroll_offset @@ -1714,6 +1724,10 @@ void LineEdit::insert_text_at_caret(String p_text) { input_direction = (TextDirection)dir; } set_caret_column(caret_column + p_text.length()); + + if (!ime_text.is_empty()) { + _shape(); + } } void LineEdit::clear_internal() { @@ -2063,7 +2077,9 @@ bool LineEdit::is_menu_visible() const { } PopupMenu *LineEdit::get_menu() const { - const_cast<LineEdit *>(this)->_ensure_menu(); + if (!menu) { + const_cast<LineEdit *>(this)->_generate_context_menu(); + } return menu; } @@ -2321,6 +2337,115 @@ Key LineEdit::_get_menu_action_accelerator(const String &p_action) { } } +void LineEdit::_generate_context_menu() { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); + menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); + + menu->add_item(RTR("Cut"), MENU_CUT); + menu->add_item(RTR("Copy"), MENU_COPY); + menu->add_item(RTR("Paste"), MENU_PASTE); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL); + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO); + menu->add_item(RTR("Redo"), MENU_REDO); + menu->add_separator(); + menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR); + menu->add_separator(); + menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); + menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC); + + menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); + + menu->connect(SNAME("focus_entered"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); + menu->connect(SNAME("focus_exited"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); +} + +void LineEdit::_update_context_menu() { + if (!menu) { + _generate_context_menu(); + } + + int idx = -1; + +#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_ACTION(m_menu, m_id, m_action) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + } + +#define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_checked(idx, m_checked); \ + } + + MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable) + MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy") + MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selecting_enabled) + MENU_ITEM_DISABLED(menu, MENU_CLEAR, !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_UNDO, "ui_undo", !editable || !has_undo()) + MENU_ITEM_ACTION_DISABLED(menu, MENU_REDO, "ui_redo", !editable || !has_redo()) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_INHERITED, text_direction == TEXT_DIRECTION_INHERITED) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_AUTO, text_direction == TEXT_DIRECTION_AUTO) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_LTR, text_direction == TEXT_DIRECTION_LTR) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_RTL, text_direction == TEXT_DIRECTION_RTL) + MENU_ITEM_CHECKED(menu, MENU_DISPLAY_UCC, draw_control_chars) + MENU_ITEM_DISABLED(menu, MENU_SUBMENU_INSERT_UCC, !editable) + +#undef MENU_ITEM_ACTION_DISABLED +#undef MENU_ITEM_ACTION +#undef MENU_ITEM_DISABLED +#undef MENU_ITEM_CHECKED +} + void LineEdit::_validate_property(PropertyInfo &p_property) const { if (!caret_blink_enabled && p_property.name == "caret_blink_interval") { p_property.usage = PROPERTY_USAGE_NO_EDITOR; @@ -2415,11 +2540,13 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_SUBMENU_TEXT_DIR); BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); BIND_ENUM_CONSTANT(MENU_DIR_AUTO); BIND_ENUM_CONSTANT(MENU_DIR_LTR); BIND_ENUM_CONSTANT(MENU_DIR_RTL); BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_SUBMENU_INSERT_UCC); BIND_ENUM_CONSTANT(MENU_INSERT_LRM); BIND_ENUM_CONSTANT(MENU_INSERT_RLM); BIND_ENUM_CONSTANT(MENU_INSERT_LRE); @@ -2482,86 +2609,6 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); } -void LineEdit::_ensure_menu() { - if (!menu) { - menu = memnew(PopupMenu); - add_child(menu, false, INTERNAL_MODE_FRONT); - - menu_dir = memnew(PopupMenu); - menu_dir->set_name("DirMenu"); - menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); - menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); - menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); - menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); - menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); - - menu_ctl = memnew(PopupMenu); - menu_ctl->set_name("CTLMenu"); - menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); - menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); - menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); - menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); - menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); - menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); - menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); - menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); - menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); - menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); - menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); - menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); - menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); - menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); - menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); - - menu->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu->connect(SNAME("focus_entered"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); - menu->connect(SNAME("focus_exited"), callable_mp(this, &LineEdit::_validate_caret_can_draw)); - menu_dir->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - menu_ctl->connect("id_pressed", callable_mp(this, &LineEdit::menu_option)); - } - - // Reorganize context menu. - menu->clear(); - if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE); - } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); - } - menu->add_separator(); - if (is_selecting_enabled()) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); - } - if (editable) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE); - } - menu->add_separator(); - menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu"); - menu->add_separator(); - menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); - if (editable) { - menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu"); - } - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); - - if (editable) { - menu->set_item_disabled(menu->get_item_index(MENU_UNDO), !has_undo()); - menu->set_item_disabled(menu->get_item_index(MENU_REDO), !has_redo()); - } -} - LineEdit::LineEdit(const String &p_placeholder) { text_rid = TS->create_shaped_text(); _create_undo_state(); @@ -2576,7 +2623,7 @@ LineEdit::LineEdit(const String &p_placeholder) { set_placeholder(p_placeholder); - set_editable(true); // Initialise to opposite first, so we get past the early-out in set_editable. + set_editable(true); // Initialize to opposite first, so we get past the early-out in set_editable. } LineEdit::~LineEdit() { diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 5107845a5e..81c506069a 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -46,11 +46,13 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_SUBMENU_TEXT_DIR, MENU_DIR_INHERITED, MENU_DIR_AUTO, MENU_DIR_LTR, MENU_DIR_RTL, MENU_DISPLAY_UCC, + MENU_SUBMENU_INSERT_UCC, MENU_INSERT_LRM, MENU_INSERT_RLM, MENU_INSERT_LRE, @@ -207,6 +209,8 @@ private: void _create_undo_state(); Key _get_menu_action_accelerator(const String &p_action); + void _generate_context_menu(); + void _update_context_menu(); void _shape(); void _fit_to_width(); @@ -239,8 +243,6 @@ private: void _backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); - void _ensure_menu(); - protected: bool _is_over_clear_button(const Point2 &p_pos) const; virtual void _update_theme_item_cache() override; diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp index 224e405d41..5b27983851 100644 --- a/scene/gui/menu_bar.cpp +++ b/scene/gui/menu_bar.cpp @@ -214,10 +214,10 @@ void MenuBar::_update_submenu(const String &p_menu_name, PopupMenu *p_child) { PopupMenu *pm = Object::cast_to<PopupMenu>(n); ERR_FAIL_COND_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, p_child->get_item_text(i), p_menu_name + "/" + itos(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, p_child->get_item_text(i), callable_mp(p_child, &PopupMenu::activate_item), Callable(), i); + 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); @@ -290,7 +290,7 @@ void MenuBar::_update_menu() { if (menu_cache[i].hidden) { continue; } - String menu_name = String(popups[i]->get_meta("_menu_name", popups[i]->get_name())); + 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) { @@ -525,7 +525,7 @@ void MenuBar::shape(Menu &p_menu) { } else { p_menu.text_buf->set_direction((TextServer::Direction)text_direction); } - p_menu.text_buf->add_string(p_menu.name, theme_cache.font, theme_cache.font_size, language); + p_menu.text_buf->add_string(atr(p_menu.name), theme_cache.font, theme_cache.font_size, language); } void MenuBar::_refresh_menu_names() { diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index c939e7a48a..2ea1b93810 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -59,9 +59,9 @@ void Popup::_initialize_visible_parents() { void Popup::_deinitialize_visible_parents() { if (is_embedded()) { - for (uint32_t i = 0; i < visible_parents.size(); ++i) { - visible_parents[i]->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); - visible_parents[i]->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); + for (Window *parent_window : visible_parents) { + parent_window->disconnect("focus_entered", callable_mp(this, &Popup::_parent_focused)); + parent_window->disconnect("tree_exited", callable_mp(this, &Popup::_deinitialize_visible_parents)); } visible_parents.clear(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 4e8a44dd63..ddc11d97b9 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -594,17 +594,17 @@ void PopupMenu::_draw_items() { int content_left = content_center - content_size / 2; int content_right = content_center + content_size / 2; if (content_left > item_ofs.x) { - int sep_h = theme_cache.labeled_separator_left->get_center_size().height + theme_cache.labeled_separator_left->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_left->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); theme_cache.labeled_separator_left->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(MAX(0, content_left - item_ofs.x), sep_h))); } if (content_right < display_width) { - int sep_h = theme_cache.labeled_separator_right->get_center_size().height + theme_cache.labeled_separator_right->get_minimum_size().height; + int sep_h = theme_cache.labeled_separator_right->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); theme_cache.labeled_separator_right->draw(ci, Rect2(Point2(content_right, item_ofs.y + sep_ofs), Size2(MAX(0, display_width - content_right), sep_h))); } } else { - int sep_h = theme_cache.separator_style->get_center_size().height + theme_cache.separator_style->get_minimum_size().height; + int sep_h = theme_cache.separator_style->get_minimum_size().height; int sep_ofs = Math::floor((h - sep_h) / 2.0); theme_cache.separator_style->draw(ci, Rect2(item_ofs + Point2(0, sep_ofs), Size2(display_width, sep_h))); } diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 5ab64b35fd..3051502dd0 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1797,7 +1797,9 @@ void RichTextLabel::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - queue_redraw(); + if (is_visible_in_tree()) { + queue_redraw(); + } } break; case NOTIFICATION_DRAW: { @@ -2665,19 +2667,26 @@ bool RichTextLabel::_find_layout_subitem(Item *from, Item *to) { return false; } -void RichTextLabel::_thread_function(void *self) { - RichTextLabel *rtl = reinterpret_cast<RichTextLabel *>(self); - rtl->set_physics_process_internal(true); - rtl->_process_line_caches(); - rtl->set_physics_process_internal(false); - rtl->updating.store(false); - rtl->call_deferred(SNAME("queue_redraw")); +void RichTextLabel::_thread_function(void *p_userdata) { + _process_line_caches(); + updating.store(false); + call_deferred(SNAME("thread_end")); +} + +void RichTextLabel::_thread_end() { + set_physics_process_internal(false); + if (is_visible_in_tree()) { + queue_redraw(); + } } void RichTextLabel::_stop_thread() { if (threaded) { stop_thread.store(true); - thread.wait_to_finish(); + if (task != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(task); + task = WorkerThreadPool::INVALID_TASK_ID; + } } } @@ -2778,7 +2787,7 @@ bool RichTextLabel::_validate_line_caches() { main->first_resized_line.store(main->lines.size()); - if (fit_content_height) { + if (fit_content) { update_minimum_size(); } return true; @@ -2787,7 +2796,8 @@ bool RichTextLabel::_validate_line_caches() { if (threaded) { updating.store(true); loaded.store(true); - thread.start(RichTextLabel::_thread_function, reinterpret_cast<void *>(this)); + task = WorkerThreadPool::get_singleton()->add_template_task(this, &RichTextLabel::_thread_function, nullptr, true, vformat("RichTextLabelShape:%x", (int64_t)get_instance_id())); + set_physics_process_internal(true); loading_started = OS::get_singleton()->get_ticks_msec(); return false; } else { @@ -2808,7 +2818,7 @@ void RichTextLabel::_process_line_caches() { int ctrl_height = get_size().height; int fi = main->first_invalid_line.load(); - int total_chars = (fi == 0) ? 0 : (main->lines[fi].char_offset + main->lines[fi].char_count); + int total_chars = main->lines[fi].char_offset; float total_height = (fi == 0) ? 0 : _calculate_line_vertical_offset(main->lines[fi - 1]); for (int i = fi; i < (int)main->lines.size(); i++) { @@ -2862,7 +2872,7 @@ void RichTextLabel::_process_line_caches() { main->first_resized_line.store(main->lines.size()); main->first_invalid_font_line.store(main->lines.size()); - if (fit_content_height) { + if (fit_content) { update_minimum_size(); } emit_signal(SNAME("finished")); @@ -2963,7 +2973,7 @@ void RichTextLabel::_add_item(Item *p_item, bool p_enter, bool p_ensure_newline) _invalidate_current_line(current_frame); - if (fixed_width != -1) { + if (fit_content) { update_minimum_size(); } queue_redraw(); @@ -3435,6 +3445,8 @@ void RichTextLabel::push_customfx(Ref<RichTextEffect> p_custom_effect, Dictionar item->custom_effect = p_custom_effect; item->char_fx_transform->environment = p_environment; _add_item(item, true); + + set_process_internal(true); } void RichTextLabel::set_table_column_expand(int p_column, bool p_expand, int p_ratio) { @@ -3551,7 +3563,7 @@ void RichTextLabel::clear() { scroll_following = true; } - if (fixed_width != -1) { + if (fit_content) { update_minimum_size(); } } @@ -3572,15 +3584,17 @@ int RichTextLabel::get_tab_size() const { return tab_size; } -void RichTextLabel::set_fit_content_height(bool p_enabled) { - if (p_enabled != fit_content_height) { - fit_content_height = p_enabled; - update_minimum_size(); +void RichTextLabel::set_fit_content(bool p_enabled) { + if (p_enabled == fit_content) { + return; } + + fit_content = p_enabled; + update_minimum_size(); } -bool RichTextLabel::is_fit_content_height_enabled() const { - return fit_content_height; +bool RichTextLabel::is_fit_content_enabled() const { + return fit_content; } void RichTextLabel::set_meta_underline(bool p_underline) { @@ -4074,8 +4088,8 @@ void RichTextLabel::append_text(const String &p_bbcode) { st_parser_type = TextServer::STRUCTURED_TEXT_EMAIL; } else if (subtag_a[1] == "l" || subtag_a[1] == "list") { st_parser_type = TextServer::STRUCTURED_TEXT_LIST; - } else if (subtag_a[1] == "n" || subtag_a[1] == "none") { - st_parser_type = TextServer::STRUCTURED_TEXT_NONE; + } else if (subtag_a[1] == "n" || subtag_a[1] == "gdscript") { + st_parser_type = TextServer::STRUCTURED_TEXT_GDSCRIPT; } else if (subtag_a[1] == "c" || subtag_a[1] == "custom") { st_parser_type = TextServer::STRUCTURED_TEXT_CUSTOM; } @@ -4550,7 +4564,6 @@ void RichTextLabel::append_text(const String &p_bbcode) { push_customfx(effect, properties); pos = brk_end + 1; tag_stack.push_front(identifier); - set_process_internal(true); } else { add_text("["); //ignore pos = brk_pos + 1; @@ -5344,6 +5357,7 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("push_cell"), &RichTextLabel::push_cell); ClassDB::bind_method(D_METHOD("push_fgcolor", "fgcolor"), &RichTextLabel::push_fgcolor); ClassDB::bind_method(D_METHOD("push_bgcolor", "bgcolor"), &RichTextLabel::push_bgcolor); + ClassDB::bind_method(D_METHOD("push_customfx", "effect", "env"), &RichTextLabel::push_customfx); ClassDB::bind_method(D_METHOD("pop"), &RichTextLabel::pop); ClassDB::bind_method(D_METHOD("clear"), &RichTextLabel::clear); @@ -5381,8 +5395,8 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tab_size", "spaces"), &RichTextLabel::set_tab_size); ClassDB::bind_method(D_METHOD("get_tab_size"), &RichTextLabel::get_tab_size); - ClassDB::bind_method(D_METHOD("set_fit_content_height", "enabled"), &RichTextLabel::set_fit_content_height); - ClassDB::bind_method(D_METHOD("is_fit_content_height_enabled"), &RichTextLabel::is_fit_content_height_enabled); + ClassDB::bind_method(D_METHOD("set_fit_content", "enabled"), &RichTextLabel::set_fit_content); + ClassDB::bind_method(D_METHOD("is_fit_content_enabled"), &RichTextLabel::is_fit_content_enabled); ClassDB::bind_method(D_METHOD("set_selection_enabled", "enabled"), &RichTextLabel::set_selection_enabled); ClassDB::bind_method(D_METHOD("is_selection_enabled"), &RichTextLabel::is_selection_enabled); @@ -5453,11 +5467,13 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("get_menu"), &RichTextLabel::get_menu); ClassDB::bind_method(D_METHOD("is_menu_visible"), &RichTextLabel::is_menu_visible); + ClassDB::bind_method(D_METHOD("_thread_end"), &RichTextLabel::_thread_end); + // Note: set "bbcode_enabled" first, to avoid unnecessary "text" resets. ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bbcode_enabled"), "set_use_bbcode", "is_using_bbcode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content_height"), "set_fit_content_height", "is_fit_content_height_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fit_content"), "set_fit_content", "is_fit_content_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_active"), "set_scroll_active", "is_scroll_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_following"), "set_scroll_follow", "is_scroll_following"); ADD_PROPERTY(PropertyInfo(Variant::INT, "autowrap_mode", PROPERTY_HINT_ENUM, "Off,Arbitrary,Word,Word (Smart)"), "set_autowrap_mode", "get_autowrap_mode"); @@ -5644,27 +5660,17 @@ int RichTextLabel::get_total_glyph_count() const { return tg; } -void RichTextLabel::set_fixed_size_to_width(int p_width) { - if (fixed_width == p_width) { - return; - } - - fixed_width = p_width; - update_minimum_size(); -} - Size2 RichTextLabel::get_minimum_size() const { - Size2 size = theme_cache.normal_style->get_minimum_size(); - - if (fixed_width != -1) { - size.x += fixed_width; - } + Size2 sb_min_size = theme_cache.normal_style->get_minimum_size(); + Size2 min_size; - if (fit_content_height) { - size.y += get_content_height(); + if (fit_content) { + min_size.x = get_content_width(); + min_size.y = get_content_height(); } - return size; + return sb_min_size + + ((autowrap_mode != TextServer::AUTOWRAP_OFF) ? Size2(1, min_size.height) : min_size); } // Context menu. diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 8ac77d5b47..fcbb91f67e 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -31,6 +31,7 @@ #ifndef RICH_TEXT_LABEL_H #define RICH_TEXT_LABEL_H +#include "core/object/worker_thread_pool.h" #include "rich_text_effect.h" #include "scene/gui/popup_menu.h" #include "scene/gui/scroll_bar.h" @@ -369,7 +370,7 @@ private: Item *current = nullptr; ItemFrame *current_frame = nullptr; - Thread thread; + WorkerThreadPool::TaskID task = WorkerThreadPool::INVALID_TASK_ID; Mutex data_mutex; bool threaded = false; std::atomic<bool> stop_thread; @@ -409,7 +410,8 @@ private: void _invalidate_current_line(ItemFrame *p_frame); - static void _thread_function(void *self); + void _thread_function(void *p_userdata); + void _thread_end(); void _stop_thread(); bool _validate_line_caches(); void _process_line_caches(); @@ -524,9 +526,7 @@ private: bool use_bbcode = false; String text; - int fixed_width = -1; - - bool fit_content_height = false; + bool fit_content = false; struct ThemeCache { Ref<StyleBox> normal_style; @@ -640,8 +640,8 @@ public: void set_shortcut_keys_enabled(bool p_enabled); bool is_shortcut_keys_enabled() const; - void set_fit_content_height(bool p_enabled); - bool is_fit_content_height_enabled() const; + void set_fit_content(bool p_enabled); + bool is_fit_content_enabled() const; bool search(const String &p_string, bool p_from_selection = false, bool p_search_previous = false); @@ -731,7 +731,6 @@ public: void install_effect(const Variant effect); - void set_fixed_size_to_width(int p_width); virtual Size2 get_minimum_size() const override; RichTextLabel(const String &p_text = String()); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index e617b2ca77..b8faf22a59 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -433,7 +433,7 @@ void ScrollBar::_notification(int p_what) { double ScrollBar::get_grabber_min_size() const { Ref<StyleBox> grabber = theme_cache.grabber_style; - Size2 gminsize = grabber->get_minimum_size() + grabber->get_center_size(); + Size2 gminsize = grabber->get_minimum_size(); return (orientation == VERTICAL) ? gminsize.height : gminsize.width; } @@ -500,7 +500,7 @@ Size2 ScrollBar::get_minimum_size() const { Size2 minsize; if (orientation == VERTICAL) { - minsize.width = MAX(incr->get_size().width, (bg->get_minimum_size() + bg->get_center_size()).width); + minsize.width = MAX(incr->get_size().width, bg->get_minimum_size().width); minsize.height += incr->get_size().height; minsize.height += decr->get_size().height; minsize.height += bg->get_minimum_size().height; @@ -508,7 +508,7 @@ Size2 ScrollBar::get_minimum_size() const { } if (orientation == HORIZONTAL) { - minsize.height = MAX(incr->get_size().height, (bg->get_center_size() + bg->get_minimum_size()).height); + minsize.height = MAX(incr->get_size().height, bg->get_minimum_size().height); minsize.width += incr->get_size().width; minsize.width += decr->get_size().width; minsize.width += bg->get_minimum_size().width; diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index 45185de698..b0879c1931 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -51,7 +51,7 @@ void Separator::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { Size2i size = get_size(); - Size2i ssize = theme_cache.separator_style->get_minimum_size() + theme_cache.separator_style->get_center_size(); + Size2i ssize = theme_cache.separator_style->get_minimum_size(); if (orientation == VERTICAL) { theme_cache.separator_style->draw(get_canvas_item(), Rect2((size.x - ssize.x) / 2, 0, ssize.x, size.y)); diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 040559dab8..292a4cfea2 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -33,7 +33,7 @@ #include "core/os/keyboard.h" Size2 Slider::get_minimum_size() const { - Size2i ss = theme_cache.slider_style->get_minimum_size() + theme_cache.slider_style->get_center_size(); + Size2i ss = theme_cache.slider_style->get_minimum_size(); Size2i rs = theme_cache.grabber_icon->get_size(); if (orientation == HORIZONTAL) { @@ -212,7 +212,7 @@ void Slider::_notification(int p_what) { } if (orientation == VERTICAL) { - int widget_width = style->get_minimum_size().width + style->get_center_size().width; + int widget_width = style->get_minimum_size().width; double areasize = size.height - grabber->get_size().height; style->draw(ci, Rect2i(Point2i(size.width / 2 - widget_width / 2, 0), Size2i(widget_width, size.height))); grabber_area->draw(ci, Rect2i(Point2i((size.width - widget_width) / 2, size.height - areasize * ratio - grabber->get_size().height / 2), Size2i(widget_width, areasize * ratio + grabber->get_size().height / 2))); @@ -229,7 +229,7 @@ void Slider::_notification(int p_what) { } grabber->draw(ci, Point2i(size.width / 2 - grabber->get_size().width / 2 + get_theme_constant(SNAME("grabber_offset")), size.height - ratio * areasize - grabber->get_size().height)); } else { - int widget_height = style->get_minimum_size().height + style->get_center_size().height; + int widget_height = style->get_minimum_size().height; double areasize = size.width - grabber->get_size().width; style->draw(ci, Rect2i(Point2i(0, (size.height - widget_height) / 2), Size2i(size.width, widget_height))); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index dfc03c666b..f99b2edd54 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -368,7 +368,7 @@ void SpinBox::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "update_on_text_changed"), "set_update_on_text_changed", "get_update_on_text_changed"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "prefix"), "set_prefix", "get_prefix"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "suffix"), "set_suffix", "get_suffix"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step"), "set_custom_arrow_step", "get_custom_arrow_step"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); } diff --git a/scene/gui/subviewport_container.cpp b/scene/gui/subviewport_container.cpp index 440597c24a..f10e1c2cd1 100644 --- a/scene/gui/subviewport_container.cpp +++ b/scene/gui/subviewport_container.cpp @@ -85,7 +85,7 @@ void SubViewportContainer::set_stretch_shrink(int p_shrink) { continue; } - c->set_size(get_size() / shrink); + c->set_size_force(get_size() / shrink); } queue_redraw(); @@ -116,7 +116,7 @@ void SubViewportContainer::_notification(int p_what) { continue; } - c->set_size(get_size() / shrink); + c->set_size_force(get_size() / shrink); } } break; @@ -180,24 +180,51 @@ void SubViewportContainer::input(const Ref<InputEvent> &p_event) { return; } - Transform2D xform = get_global_transform_with_canvas(); + if (_is_propagated_in_gui_input(p_event)) { + return; + } - if (stretch) { - Transform2D scale_xf; - scale_xf.scale(Vector2(shrink, shrink)); - xform *= scale_xf; + _send_event_to_viewports(p_event); +} + +void SubViewportContainer::gui_input(const Ref<InputEvent> &p_event) { + ERR_FAIL_COND(p_event.is_null()); + + if (Engine::get_singleton()->is_editor_hint()) { + return; } - Ref<InputEvent> ev = p_event->xformed_by(xform.affine_inverse()); + if (!_is_propagated_in_gui_input(p_event)) { + return; + } + if (stretch && shrink > 1) { + Transform2D xform; + xform.scale(Vector2(1, 1) / shrink); + _send_event_to_viewports(p_event->xformed_by(xform)); + } else { + _send_event_to_viewports(p_event); + } +} + +void SubViewportContainer::_send_event_to_viewports(const Ref<InputEvent> &p_event) { for (int i = 0; i < get_child_count(); i++) { SubViewport *c = Object::cast_to<SubViewport>(get_child(i)); if (!c || c->is_input_disabled()) { continue; } - c->push_input(ev); + c->push_input(p_event); + } +} + +bool SubViewportContainer::_is_propagated_in_gui_input(const Ref<InputEvent> &p_event) { + // Propagation of events with a position property happen in gui_input + // Propagation of other events happen in input + if (Object::cast_to<InputEventMouse>(*p_event) || Object::cast_to<InputEventScreenDrag>(*p_event) || Object::cast_to<InputEventScreenTouch>(*p_event) || Object::cast_to<InputEventGesture>(*p_event)) { + return true; } + return false; } void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) { diff --git a/scene/gui/subviewport_container.h b/scene/gui/subviewport_container.h index d918c4a615..d3236b0c4e 100644 --- a/scene/gui/subviewport_container.h +++ b/scene/gui/subviewport_container.h @@ -39,6 +39,8 @@ class SubViewportContainer : public Container { bool stretch = false; int shrink = 1; void _notify_viewports(int p_notification); + bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event); + void _send_event_to_viewports(const Ref<InputEvent> &p_event); protected: void _notification(int p_what); @@ -52,6 +54,7 @@ public: bool is_stretch_enabled() const; virtual void input(const Ref<InputEvent> &p_event) override; + virtual void gui_input(const Ref<InputEvent> &p_event) override; virtual void unhandled_input(const Ref<InputEvent> &p_event) override; void set_stretch_shrink(int p_shrink); int get_stretch_shrink() const; diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 3457cfa94f..208cb29772 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -954,9 +954,6 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("_repaint"), &TabContainer::_repaint); ClassDB::bind_method(D_METHOD("_on_theme_changed"), &TabContainer::_on_theme_changed); - ClassDB::bind_method(D_METHOD("_get_drag_data_fw"), &TabContainer::_get_drag_data_fw); - ClassDB::bind_method(D_METHOD("_can_drop_data_fw"), &TabContainer::_can_drop_data_fw); - ClassDB::bind_method(D_METHOD("_drop_data_fw"), &TabContainer::_drop_data_fw); ADD_SIGNAL(MethodInfo("tab_changed", PropertyInfo(Variant::INT, "tab"))); ADD_SIGNAL(MethodInfo("tab_selected", PropertyInfo(Variant::INT, "tab"))); @@ -975,7 +972,7 @@ void TabContainer::_bind_methods() { TabContainer::TabContainer() { tab_bar = memnew(TabBar); - tab_bar->set_drag_forwarding_compat(this); + SET_DRAG_FORWARDING_GCDU(tab_bar, TabContainer); add_child(tab_bar, false, INTERNAL_MODE_FRONT); tab_bar->set_anchors_and_offsets_preset(Control::PRESET_TOP_WIDE); tab_bar->connect("tab_changed", callable_mp(this, &TabContainer::_on_tab_changed)); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 108a533a74..d785280701 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1208,7 +1208,15 @@ void TextEdit::_notification(int p_what) { char_ofs = 0; } for (int j = 0; j < gl_size; j++) { - const Variant *color_data = color_map.getptr(glyphs[j].start); + int64_t color_start = -1; + for (const Variant *key = color_map.next(nullptr); key; key = color_map.next(key)) { + if (int64_t(*key) <= glyphs[j].start) { + color_start = *key; + } else { + break; + } + } + const Variant *color_data = (color_start >= 0) ? color_map.getptr(color_start) : nullptr; if (color_data != nullptr) { current_color = (color_data->operator Dictionary()).get("color", font_color); if (!editable && current_color.a > font_readonly_color.a) { @@ -1311,11 +1319,12 @@ void TextEdit::_notification(int p_what) { if (!clipped && get_caret_line(c) == line && carets_wrap_index[c] == line_wrap_index) { carets.write[c].draw_pos.y = ofs_y + ldata->get_line_descent(line_wrap_index); - if (ime_text.length() == 0) { + if (ime_text.is_empty() || ime_selection.y == 0) { + // Normal caret. CaretInfo ts_caret; - if (str.length() != 0) { + if (!str.is_empty() || !ime_text.is_empty()) { // Get carets. - ts_caret = TS->shaped_text_get_carets(rid, get_caret_column(c)); + ts_caret = TS->shaped_text_get_carets(rid, ime_text.is_empty() ? get_caret_column(c) : get_caret_column(c) + ime_selection.x); } else { // No carets, add one at the start. int h = font->get_height(font_size); @@ -1418,7 +1427,8 @@ void TextEdit::_notification(int p_what) { } } } - } else { + } + if (!ime_text.is_empty()) { { // IME Intermediate text range. Vector<Vector2> sel = TS->shaped_text_get_selection(rid, get_caret_column(c), get_caret_column(c) + ime_text.length()); @@ -1538,6 +1548,10 @@ void TextEdit::_notification(int p_what) { ime_text = DisplayServer::get_singleton()->ime_get_text(); ime_selection = DisplayServer::get_singleton()->ime_get_selection(); + if (!ime_text.is_empty()) { + delete_selection(); + } + for (int i = 0; i < carets.size(); i++) { String t; if (get_caret_column(i) >= 0) { @@ -1847,7 +1861,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); menu->set_position(get_screen_position() + mpos); menu->reset_size(); menu->popup(); @@ -2127,7 +2141,7 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { // MISC. if (k->is_action("ui_menu", true)) { if (context_menu_enabled) { - _generate_context_menu(); + _update_context_menu(); adjust_viewport_to_caret(); menu->set_position(get_screen_position() + get_caret_draw_pos()); menu->reset_size(); @@ -3530,6 +3544,19 @@ void TextEdit::insert_text_at_caret(const String &p_text, int p_caret) { adjust_carets_after_edit(i, new_line, new_column, from_line, from_col); } + + if (!ime_text.is_empty()) { + for (int i = 0; i < carets.size(); i++) { + String t; + if (get_caret_column(i) >= 0) { + t = text[get_caret_line(i)].substr(0, get_caret_column(i)) + ime_text + text[get_caret_line(i)].substr(get_caret_column(i), text[get_caret_line(i)].length()); + } else { + t = ime_text; + } + text.invalidate_cache(get_caret_line(i), get_caret_column(i), true, t, structured_text_parser(st_parser, st_args, t)); + } + } + end_complex_operation(); queue_redraw(); } @@ -3699,7 +3726,9 @@ void TextEdit::paste_primary_clipboard(int p_caret) { // Context menu. PopupMenu *TextEdit::get_menu() const { - const_cast<TextEdit *>(this)->_generate_context_menu(); + if (!menu) { + const_cast<TextEdit *>(this)->_generate_context_menu(); + } return menu; } @@ -6048,11 +6077,13 @@ void TextEdit::_bind_methods() { BIND_ENUM_CONSTANT(MENU_SELECT_ALL); BIND_ENUM_CONSTANT(MENU_UNDO); BIND_ENUM_CONSTANT(MENU_REDO); + BIND_ENUM_CONSTANT(MENU_SUBMENU_TEXT_DIR); BIND_ENUM_CONSTANT(MENU_DIR_INHERITED); BIND_ENUM_CONSTANT(MENU_DIR_AUTO); BIND_ENUM_CONSTANT(MENU_DIR_LTR); BIND_ENUM_CONSTANT(MENU_DIR_RTL); BIND_ENUM_CONSTANT(MENU_DISPLAY_UCC); + BIND_ENUM_CONSTANT(MENU_SUBMENU_INSERT_UCC); BIND_ENUM_CONSTANT(MENU_INSERT_LRM); BIND_ENUM_CONSTANT(MENU_INSERT_RLM); BIND_ENUM_CONSTANT(MENU_INSERT_LRE); @@ -6360,7 +6391,7 @@ void TextEdit::_bind_methods() { 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_DO_NOT_SHARE_ON_DUPLICATE), "set_syntax_highlighter", "get_syntax_highlighter"); + 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"); @@ -6703,87 +6734,7 @@ void TextEdit::_paste_primary_clipboard_internal(int p_caret) { grab_focus(); } -/* Text. */ // Context menu. -void TextEdit::_generate_context_menu() { - if (!menu) { - menu = memnew(PopupMenu); - add_child(menu, false, INTERNAL_MODE_FRONT); - - menu_dir = memnew(PopupMenu); - menu_dir->set_name("DirMenu"); - menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); - menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); - menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); - menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); - menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); - - menu_ctl = memnew(PopupMenu); - menu_ctl->set_name("CTLMenu"); - menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); - menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); - menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); - menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); - menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); - menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); - menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); - menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); - menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); - menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); - menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); - menu_ctl->add_separator(); - menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); - menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); - menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); - menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); - menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); - - menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); - } - - // Reorganize context menu. - menu->clear(); - if (editable) { - menu->add_item(RTR("Cut"), MENU_CUT, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_cut") : Key::NONE); - } - menu->add_item(RTR("Copy"), MENU_COPY, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_copy") : Key::NONE); - if (editable) { - menu->add_item(RTR("Paste"), MENU_PASTE, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_paste") : Key::NONE); - } - if (selecting_enabled || editable) { - menu->add_separator(); - } - if (selecting_enabled) { - menu->add_item(RTR("Select All"), MENU_SELECT_ALL, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_text_select_all") : Key::NONE); - } - if (editable) { - menu->add_item(RTR("Clear"), MENU_CLEAR); - menu->add_separator(); - menu->add_item(RTR("Undo"), MENU_UNDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_undo") : Key::NONE); - menu->add_item(RTR("Redo"), MENU_REDO, is_shortcut_keys_enabled() ? _get_menu_action_accelerator("ui_redo") : Key::NONE); - } - menu->add_separator(); - menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu"); - menu->add_separator(); - menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); - menu->set_item_checked(menu->get_item_index(MENU_DISPLAY_UCC), draw_control_chars); - if (editable) { - menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu"); - } - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_INHERITED), text_direction == TEXT_DIRECTION_INHERITED); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_AUTO), text_direction == TEXT_DIRECTION_AUTO); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_LTR), text_direction == TEXT_DIRECTION_LTR); - menu_dir->set_item_checked(menu_dir->get_item_index(MENU_DIR_RTL), text_direction == TEXT_DIRECTION_RTL); - - if (editable) { - menu->set_item_disabled(menu->get_item_index(MENU_UNDO), !has_undo()); - menu->set_item_disabled(menu->get_item_index(MENU_REDO), !has_redo()); - } -} Key TextEdit::_get_menu_action_accelerator(const String &p_action) { const List<Ref<InputEvent>> *events = InputMap::get_singleton()->action_get_events(p_action); @@ -6810,6 +6761,112 @@ Key TextEdit::_get_menu_action_accelerator(const String &p_action) { } } +void TextEdit::_generate_context_menu() { + menu = memnew(PopupMenu); + add_child(menu, false, INTERNAL_MODE_FRONT); + + menu_dir = memnew(PopupMenu); + menu_dir->set_name("DirMenu"); + menu_dir->add_radio_check_item(RTR("Same as Layout Direction"), MENU_DIR_INHERITED); + menu_dir->add_radio_check_item(RTR("Auto-Detect Direction"), MENU_DIR_AUTO); + menu_dir->add_radio_check_item(RTR("Left-to-Right"), MENU_DIR_LTR); + menu_dir->add_radio_check_item(RTR("Right-to-Left"), MENU_DIR_RTL); + menu->add_child(menu_dir, false, INTERNAL_MODE_FRONT); + + menu_ctl = memnew(PopupMenu); + menu_ctl->set_name("CTLMenu"); + menu_ctl->add_item(RTR("Left-to-Right Mark (LRM)"), MENU_INSERT_LRM); + menu_ctl->add_item(RTR("Right-to-Left Mark (RLM)"), MENU_INSERT_RLM); + menu_ctl->add_item(RTR("Start of Left-to-Right Embedding (LRE)"), MENU_INSERT_LRE); + menu_ctl->add_item(RTR("Start of Right-to-Left Embedding (RLE)"), MENU_INSERT_RLE); + menu_ctl->add_item(RTR("Start of Left-to-Right Override (LRO)"), MENU_INSERT_LRO); + menu_ctl->add_item(RTR("Start of Right-to-Left Override (RLO)"), MENU_INSERT_RLO); + menu_ctl->add_item(RTR("Pop Direction Formatting (PDF)"), MENU_INSERT_PDF); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Arabic Letter Mark (ALM)"), MENU_INSERT_ALM); + menu_ctl->add_item(RTR("Left-to-Right Isolate (LRI)"), MENU_INSERT_LRI); + menu_ctl->add_item(RTR("Right-to-Left Isolate (RLI)"), MENU_INSERT_RLI); + menu_ctl->add_item(RTR("First Strong Isolate (FSI)"), MENU_INSERT_FSI); + menu_ctl->add_item(RTR("Pop Direction Isolate (PDI)"), MENU_INSERT_PDI); + menu_ctl->add_separator(); + menu_ctl->add_item(RTR("Zero-Width Joiner (ZWJ)"), MENU_INSERT_ZWJ); + menu_ctl->add_item(RTR("Zero-Width Non-Joiner (ZWNJ)"), MENU_INSERT_ZWNJ); + menu_ctl->add_item(RTR("Word Joiner (WJ)"), MENU_INSERT_WJ); + menu_ctl->add_item(RTR("Soft Hyphen (SHY)"), MENU_INSERT_SHY); + menu->add_child(menu_ctl, false, INTERNAL_MODE_FRONT); + + menu->add_item(RTR("Cut"), MENU_CUT); + menu->add_item(RTR("Copy"), MENU_COPY); + menu->add_item(RTR("Paste"), MENU_PASTE); + menu->add_separator(); + menu->add_item(RTR("Select All"), MENU_SELECT_ALL); + menu->add_item(RTR("Clear"), MENU_CLEAR); + menu->add_separator(); + menu->add_item(RTR("Undo"), MENU_UNDO); + menu->add_item(RTR("Redo"), MENU_REDO); + menu->add_separator(); + menu->add_submenu_item(RTR("Text Writing Direction"), "DirMenu", MENU_SUBMENU_TEXT_DIR); + menu->add_separator(); + menu->add_check_item(RTR("Display Control Characters"), MENU_DISPLAY_UCC); + menu->add_submenu_item(RTR("Insert Control Character"), "CTLMenu", MENU_SUBMENU_INSERT_UCC); + + menu->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_dir->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); + menu_ctl->connect("id_pressed", callable_mp(this, &TextEdit::menu_option)); +} + +void TextEdit::_update_context_menu() { + if (!menu) { + _generate_context_menu(); + } + + int idx = -1; + +#define MENU_ITEM_ACTION_DISABLED(m_menu, m_id, m_action, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_ACTION(m_menu, m_id, m_action) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_accelerator(idx, shortcut_keys_enabled ? _get_menu_action_accelerator(m_action) : Key::NONE); \ + } + +#define MENU_ITEM_DISABLED(m_menu, m_id, m_disabled) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_disabled(idx, m_disabled); \ + } + +#define MENU_ITEM_CHECKED(m_menu, m_id, m_checked) \ + idx = m_menu->get_item_index(m_id); \ + if (idx >= 0) { \ + m_menu->set_item_checked(idx, m_checked); \ + } + + MENU_ITEM_ACTION_DISABLED(menu, MENU_CUT, "ui_cut", !editable) + MENU_ITEM_ACTION(menu, MENU_COPY, "ui_copy") + MENU_ITEM_ACTION_DISABLED(menu, MENU_PASTE, "ui_paste", !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_SELECT_ALL, "ui_text_select_all", !selecting_enabled) + MENU_ITEM_DISABLED(menu, MENU_CLEAR, !editable) + MENU_ITEM_ACTION_DISABLED(menu, MENU_UNDO, "ui_undo", !editable || !has_undo()) + MENU_ITEM_ACTION_DISABLED(menu, MENU_REDO, "ui_redo", !editable || !has_redo()) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_INHERITED, text_direction == TEXT_DIRECTION_INHERITED) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_AUTO, text_direction == TEXT_DIRECTION_AUTO) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_LTR, text_direction == TEXT_DIRECTION_LTR) + MENU_ITEM_CHECKED(menu_dir, MENU_DIR_RTL, text_direction == TEXT_DIRECTION_RTL) + MENU_ITEM_CHECKED(menu, MENU_DISPLAY_UCC, draw_control_chars) + MENU_ITEM_DISABLED(menu, MENU_SUBMENU_INSERT_UCC, !editable) + +#undef MENU_ITEM_ACTION_DISABLED +#undef MENU_ITEM_ACTION +#undef MENU_ITEM_DISABLED +#undef MENU_ITEM_CHECKED +} + /* Versioning */ void TextEdit::_push_current_op() { if (pending_action_end) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 426acc4996..a084fa3833 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -87,11 +87,13 @@ public: MENU_SELECT_ALL, MENU_UNDO, MENU_REDO, + MENU_SUBMENU_TEXT_DIR, MENU_DIR_INHERITED, MENU_DIR_AUTO, MENU_DIR_LTR, MENU_DIR_RTL, MENU_DISPLAY_UCC, + MENU_SUBMENU_INSERT_UCC, MENU_INSERT_LRM, MENU_INSERT_RLM, MENU_INSERT_LRE, @@ -274,7 +276,7 @@ private: void _update_placeholder(); - /* Initialise to opposite first, so we get past the early-out in set_editable. */ + /* Initialize to opposite first, so we get past the early-out in set_editable. */ bool editable = false; TextDirection text_direction = TEXT_DIRECTION_AUTO; @@ -303,8 +305,9 @@ private: PopupMenu *menu_dir = nullptr; PopupMenu *menu_ctl = nullptr; - void _generate_context_menu(); Key _get_menu_action_accelerator(const String &p_action); + void _generate_context_menu(); + void _update_context_menu(); /* Versioning */ struct Caret; diff --git a/scene/gui/texture_rect.cpp b/scene/gui/texture_rect.cpp index 5a3f4af106..20472ab46e 100644 --- a/scene/gui/texture_rect.cpp +++ b/scene/gui/texture_rect.cpp @@ -178,6 +178,16 @@ void TextureRect::_bind_methods() { BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT_COVERED); } +#ifndef DISABLE_DEPRECATED +bool TextureRect::_set(const StringName &p_name, const Variant &p_value) { + if ((p_name == SNAME("expand") || p_name == SNAME("ignore_texture_size")) && p_value.operator bool()) { + expand_mode = EXPAND_IGNORE_SIZE; + return true; + } + return false; +} +#endif + void TextureRect::_texture_changed() { if (texture.is_valid()) { queue_redraw(); diff --git a/scene/gui/texture_rect.h b/scene/gui/texture_rect.h index 6f17ebd87f..2425c6094b 100644 --- a/scene/gui/texture_rect.h +++ b/scene/gui/texture_rect.h @@ -69,6 +69,9 @@ protected: void _notification(int p_what); virtual Size2 get_minimum_size() const override; static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); +#endif public: void set_texture(const Ref<Texture2D> &p_tex); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 3458b87b8d..d9e6157489 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -332,7 +332,7 @@ void TreeItem::set_structured_text_bidi_override(int p_column, TextServer::Struc } TextServer::StructuredTextParser TreeItem::get_structured_text_bidi_override(int p_column) const { - ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_NONE); + ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::STRUCTURED_TEXT_DEFAULT); return cells[p_column].st_parser; } @@ -657,7 +657,7 @@ int TreeItem::get_custom_minimum_height() const { /* Item manipulation */ -TreeItem *TreeItem::create_child(int p_idx) { +TreeItem *TreeItem::create_child(int p_index) { TreeItem *ti = memnew(TreeItem(tree)); if (tree) { ti->cells.resize(tree->columns.size()); @@ -669,7 +669,7 @@ TreeItem *TreeItem::create_child(int p_idx) { int idx = 0; while (c) { - if (idx++ == p_idx) { + if (idx++ == p_index) { c->prev = ti; ti->next = c; break; @@ -683,7 +683,7 @@ TreeItem *TreeItem::create_child(int p_idx) { ti->prev = l_prev; if (!children_cache.is_empty()) { if (ti->next) { - children_cache.insert(p_idx, ti); + children_cache.insert(p_index, ti); } else { children_cache.append(ti); } @@ -826,15 +826,15 @@ TreeItem *TreeItem::get_next_visible(bool p_wrap) { return next_item; } -TreeItem *TreeItem::get_child(int p_idx) { +TreeItem *TreeItem::get_child(int p_index) { _create_children_cache(); - if (p_idx < 0) { - p_idx += children_cache.size(); + if (p_index < 0) { + p_index += children_cache.size(); } - ERR_FAIL_INDEX_V(p_idx, children_cache.size(), nullptr); + ERR_FAIL_INDEX_V(p_index, children_cache.size(), nullptr); - return children_cache.get(p_idx); + return children_cache.get(p_index); } int TreeItem::get_visible_child_count() { @@ -1058,28 +1058,28 @@ int TreeItem::get_button_count(int p_column) const { return cells[p_column].buttons.size(); } -Ref<Texture2D> TreeItem::get_button(int p_column, int p_idx) const { +Ref<Texture2D> TreeItem::get_button(int p_column, int p_index) const { ERR_FAIL_INDEX_V(p_column, cells.size(), Ref<Texture2D>()); - ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), Ref<Texture2D>()); - return cells[p_column].buttons[p_idx].texture; + ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), Ref<Texture2D>()); + return cells[p_column].buttons[p_index].texture; } -String TreeItem::get_button_tooltip_text(int p_column, int p_idx) const { +String TreeItem::get_button_tooltip_text(int p_column, int p_index) const { ERR_FAIL_INDEX_V(p_column, cells.size(), String()); - ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), String()); - return cells[p_column].buttons[p_idx].tooltip; + ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), String()); + return cells[p_column].buttons[p_index].tooltip; } -int TreeItem::get_button_id(int p_column, int p_idx) const { +int TreeItem::get_button_id(int p_column, int p_index) const { ERR_FAIL_INDEX_V(p_column, cells.size(), -1); - ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), -1); - return cells[p_column].buttons[p_idx].id; + ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), -1); + return cells[p_column].buttons[p_index].id; } -void TreeItem::erase_button(int p_column, int p_idx) { +void TreeItem::erase_button(int p_column, int p_index) { ERR_FAIL_INDEX(p_column, cells.size()); - ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); - cells.write[p_column].buttons.remove_at(p_idx); + ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size()); + cells.write[p_column].buttons.remove_at(p_index); _changed_notify(p_column); } @@ -1094,52 +1094,52 @@ int TreeItem::get_button_by_id(int p_column, int p_id) const { return -1; } -void TreeItem::set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button) { +void TreeItem::set_button(int p_column, int p_index, const Ref<Texture2D> &p_button) { ERR_FAIL_COND(p_button.is_null()); ERR_FAIL_INDEX(p_column, cells.size()); - ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size()); - if (cells[p_column].buttons[p_idx].texture == p_button) { + if (cells[p_column].buttons[p_index].texture == p_button) { return; } - cells.write[p_column].buttons.write[p_idx].texture = p_button; + cells.write[p_column].buttons.write[p_index].texture = p_button; cells.write[p_column].cached_minimum_size_dirty = true; _changed_notify(p_column); } -void TreeItem::set_button_color(int p_column, int p_idx, const Color &p_color) { +void TreeItem::set_button_color(int p_column, int p_index, const Color &p_color) { ERR_FAIL_INDEX(p_column, cells.size()); - ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size()); - if (cells[p_column].buttons[p_idx].color == p_color) { + if (cells[p_column].buttons[p_index].color == p_color) { return; } - cells.write[p_column].buttons.write[p_idx].color = p_color; + cells.write[p_column].buttons.write[p_index].color = p_color; _changed_notify(p_column); } -void TreeItem::set_button_disabled(int p_column, int p_idx, bool p_disabled) { +void TreeItem::set_button_disabled(int p_column, int p_index, bool p_disabled) { ERR_FAIL_INDEX(p_column, cells.size()); - ERR_FAIL_INDEX(p_idx, cells[p_column].buttons.size()); + ERR_FAIL_INDEX(p_index, cells[p_column].buttons.size()); - if (cells[p_column].buttons[p_idx].disabled == p_disabled) { + if (cells[p_column].buttons[p_index].disabled == p_disabled) { return; } - cells.write[p_column].buttons.write[p_idx].disabled = p_disabled; + cells.write[p_column].buttons.write[p_index].disabled = p_disabled; cells.write[p_column].cached_minimum_size_dirty = true; _changed_notify(p_column); } -bool TreeItem::is_button_disabled(int p_column, int p_idx) const { +bool TreeItem::is_button_disabled(int p_column, int p_index) const { ERR_FAIL_INDEX_V(p_column, cells.size(), false); - ERR_FAIL_INDEX_V(p_idx, cells[p_column].buttons.size(), false); + ERR_FAIL_INDEX_V(p_index, cells[p_column].buttons.size(), false); - return cells[p_column].buttons[p_idx].disabled; + return cells[p_column].buttons[p_index].disabled; } void TreeItem::set_editable(int p_column, bool p_editable) { @@ -1497,15 +1497,15 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("add_button", "column", "button", "id", "disabled", "tooltip_text"), &TreeItem::add_button, DEFVAL(-1), DEFVAL(false), DEFVAL("")); ClassDB::bind_method(D_METHOD("get_button_count", "column"), &TreeItem::get_button_count); - ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_idx"), &TreeItem::get_button_tooltip_text); - ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_idx"), &TreeItem::get_button_id); + ClassDB::bind_method(D_METHOD("get_button_tooltip_text", "column", "button_index"), &TreeItem::get_button_tooltip_text); + ClassDB::bind_method(D_METHOD("get_button_id", "column", "button_index"), &TreeItem::get_button_id); ClassDB::bind_method(D_METHOD("get_button_by_id", "column", "id"), &TreeItem::get_button_by_id); - ClassDB::bind_method(D_METHOD("get_button", "column", "button_idx"), &TreeItem::get_button); - ClassDB::bind_method(D_METHOD("set_button", "column", "button_idx", "button"), &TreeItem::set_button); - ClassDB::bind_method(D_METHOD("erase_button", "column", "button_idx"), &TreeItem::erase_button); - ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_idx", "disabled"), &TreeItem::set_button_disabled); - ClassDB::bind_method(D_METHOD("set_button_color", "column", "button_idx", "color"), &TreeItem::set_button_color); - ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_idx"), &TreeItem::is_button_disabled); + ClassDB::bind_method(D_METHOD("get_button", "column", "button_index"), &TreeItem::get_button); + ClassDB::bind_method(D_METHOD("set_button", "column", "button_index", "button"), &TreeItem::set_button); + ClassDB::bind_method(D_METHOD("erase_button", "column", "button_index"), &TreeItem::erase_button); + ClassDB::bind_method(D_METHOD("set_button_disabled", "column", "button_index", "disabled"), &TreeItem::set_button_disabled); + ClassDB::bind_method(D_METHOD("set_button_color", "column", "button_index", "color"), &TreeItem::set_button_color); + ClassDB::bind_method(D_METHOD("is_button_disabled", "column", "button_index"), &TreeItem::is_button_disabled); ClassDB::bind_method(D_METHOD("set_tooltip_text", "column", "tooltip"), &TreeItem::set_tooltip_text); ClassDB::bind_method(D_METHOD("get_tooltip_text", "column"), &TreeItem::get_tooltip_text); @@ -1518,7 +1518,7 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disable_folding", "disable"), &TreeItem::set_disable_folding); ClassDB::bind_method(D_METHOD("is_folding_disabled"), &TreeItem::is_folding_disabled); - ClassDB::bind_method(D_METHOD("create_child", "idx"), &TreeItem::create_child, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("create_child", "index"), &TreeItem::create_child, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_tree"), &TreeItem::get_tree); ClassDB::bind_method(D_METHOD("get_next"), &TreeItem::get_next); @@ -1529,7 +1529,7 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("get_next_visible", "wrap"), &TreeItem::get_next_visible, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_prev_visible", "wrap"), &TreeItem::get_prev_visible, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_child", "idx"), &TreeItem::get_child); + ClassDB::bind_method(D_METHOD("get_child", "index"), &TreeItem::get_child); ClassDB::bind_method(D_METHOD("get_child_count"), &TreeItem::get_child_count); ClassDB::bind_method(D_METHOD("get_children"), &TreeItem::get_children); ClassDB::bind_method(D_METHOD("get_index"), &TreeItem::get_index); @@ -3554,7 +3554,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { icon_size_x = icon_region.size.width; } } - // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_double_clicked signal. + // Icon is treated as if it is outside of the rect so that double clicking on it will emit the item_icon_double_clicked signal. if (rtl) { mpos.x = get_size().width - (mpos.x + icon_size_x); } else { @@ -3562,10 +3562,10 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) { } if (rect.has_point(mpos)) { if (!edit_selected()) { - emit_signal(SNAME("item_double_clicked")); + emit_signal(SNAME("item_icon_double_clicked")); } } else { - emit_signal(SNAME("item_double_clicked")); + emit_signal(SNAME("item_icon_double_clicked")); } } pressing_for_editor = false; @@ -4108,14 +4108,14 @@ Size2 Tree::get_minimum_size() const { } } -TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { +TreeItem *Tree::create_item(TreeItem *p_parent, int p_index) { ERR_FAIL_COND_V(blocked > 0, nullptr); TreeItem *ti = nullptr; if (p_parent) { ERR_FAIL_COND_V_MSG(p_parent->tree != this, nullptr, "A different tree owns the given parent"); - ti = p_parent->create_child(p_idx); + ti = p_parent->create_child(p_index); } else { if (!root) { // No root exists, make the given item the new root. @@ -4126,7 +4126,7 @@ TreeItem *Tree::create_item(TreeItem *p_parent, int p_idx) { root = ti; } else { // Root exists, append or insert to root. - ti = create_item(root, p_idx); + ti = create_item(root, p_index); } } @@ -5162,7 +5162,7 @@ bool Tree::get_allow_reselect() const { void Tree::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &Tree::clear); - ClassDB::bind_method(D_METHOD("create_item", "parent", "idx"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("create_item", "parent", "index"), &Tree::create_item, DEFVAL(Variant()), DEFVAL(-1)); ClassDB::bind_method(D_METHOD("get_root"), &Tree::get_root); ClassDB::bind_method(D_METHOD("set_column_custom_minimum_width", "column", "min_width"), &Tree::set_column_custom_minimum_width); @@ -5257,7 +5257,7 @@ void Tree::_bind_methods() { ADD_SIGNAL(MethodInfo("item_edited")); ADD_SIGNAL(MethodInfo("custom_item_clicked", PropertyInfo(Variant::INT, "mouse_button_index"))); ADD_SIGNAL(MethodInfo("item_custom_button_pressed")); - ADD_SIGNAL(MethodInfo("item_double_clicked")); + ADD_SIGNAL(MethodInfo("item_icon_double_clicked")); ADD_SIGNAL(MethodInfo("item_collapsed", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"))); ADD_SIGNAL(MethodInfo("check_propagated_to_item", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"))); ADD_SIGNAL(MethodInfo("button_clicked", PropertyInfo(Variant::OBJECT, "item", PROPERTY_HINT_RESOURCE_TYPE, "TreeItem"), PropertyInfo(Variant::INT, "column"), PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "mouse_button_index"))); diff --git a/scene/gui/tree.h b/scene/gui/tree.h index e0ed5fdfb5..ec639ce439 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -247,15 +247,15 @@ public: void add_button(int p_column, const Ref<Texture2D> &p_button, int p_id = -1, bool p_disabled = false, const String &p_tooltip = ""); int get_button_count(int p_column) const; - String get_button_tooltip_text(int p_column, int p_idx) const; - Ref<Texture2D> get_button(int p_column, int p_idx) const; - int get_button_id(int p_column, int p_idx) const; - void erase_button(int p_column, int p_idx); + String get_button_tooltip_text(int p_column, int p_index) const; + Ref<Texture2D> get_button(int p_column, int p_index) const; + int get_button_id(int p_column, int p_index) const; + void erase_button(int p_column, int p_index); int get_button_by_id(int p_column, int p_id) const; - void set_button(int p_column, int p_idx, const Ref<Texture2D> &p_button); - void set_button_color(int p_column, int p_idx, const Color &p_color); - void set_button_disabled(int p_column, int p_idx, bool p_disabled); - bool is_button_disabled(int p_column, int p_idx) const; + void set_button(int p_column, int p_index, const Ref<Texture2D> &p_button); + void set_button_color(int p_column, int p_index, const Color &p_color); + void set_button_disabled(int p_column, int p_index, bool p_disabled); + bool is_button_disabled(int p_column, int p_index) const; /* range works for mode number or mode combo */ @@ -329,7 +329,7 @@ public: /* Item manipulation */ - TreeItem *create_child(int p_idx = -1); + TreeItem *create_child(int p_index = -1); Tree *get_tree() const; @@ -341,7 +341,7 @@ public: TreeItem *get_prev_visible(bool p_wrap = false); TreeItem *get_next_visible(bool p_wrap = false); - TreeItem *get_child(int p_idx); + TreeItem *get_child(int p_index); int get_visible_child_count(); int get_child_count(); TypedArray<TreeItem> get_children(); @@ -647,7 +647,7 @@ public: void clear(); - TreeItem *create_item(TreeItem *p_parent = nullptr, int p_idx = -1); + TreeItem *create_item(TreeItem *p_parent = nullptr, int p_index = -1); TreeItem *get_root() const; TreeItem *get_last_item() const; diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp index 6eb25bf852..1f3bbff779 100644 --- a/scene/gui/video_stream_player.cpp +++ b/scene/gui/video_stream_player.cpp @@ -236,7 +236,6 @@ void VideoStreamPlayer::set_stream(const Ref<VideoStream> &p_stream) { AudioServer::get_singleton()->unlock(); if (!playback.is_null()) { - playback->set_loop(loops); playback->set_paused(paused); texture = playback->get_texture(); @@ -344,6 +343,12 @@ int VideoStreamPlayer::get_buffering_msec() const { void VideoStreamPlayer::set_audio_track(int p_track) { audio_track = p_track; + if (stream.is_valid()) { + stream->set_audio_track(audio_track); + } + if (playback.is_valid()) { + playback->set_audio_track(audio_track); + } } int VideoStreamPlayer::get_audio_track() const { diff --git a/scene/gui/video_stream_player.h b/scene/gui/video_stream_player.h index 09ef272a9a..1fd599a9e1 100644 --- a/scene/gui/video_stream_player.h +++ b/scene/gui/video_stream_player.h @@ -65,7 +65,6 @@ class VideoStreamPlayer : public Control { float volume = 1.0; double last_audio_time = 0.0; bool expand = false; - bool loops = false; int buffering_ms = 500; int audio_track = 0; int bus_index = 0; diff --git a/scene/gui/view_panner.cpp b/scene/gui/view_panner.cpp index e8d54e6937..145497fa61 100644 --- a/scene/gui/view_panner.cpp +++ b/scene/gui/view_panner.cpp @@ -43,36 +43,42 @@ bool ViewPanner::gui_input(const Ref<InputEvent> &p_event, Rect2 p_canvas_rect) if (scroll_vec != Vector2() && mb->is_pressed()) { if (control_scheme == SCROLL_PANS) { if (mb->is_ctrl_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_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)); return true; } else { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } } else { if (mb->is_ctrl_pressed()) { - Vector2 panning; - if (mb->is_shift_pressed()) { - panning.x += mb->get_factor() * scroll_vec.y; - panning.y += mb->get_factor() * scroll_vec.x; - } else { - panning.y += mb->get_factor() * scroll_vec.y; - panning.x += mb->get_factor() * scroll_vec.x; + Vector2 panning = scroll_vec * mb->get_factor(); + if (pan_axis == PAN_AXIS_HORIZONTAL) { + panning = Vector2(panning.x + panning.y, 0); + } else if (pan_axis == PAN_AXIS_VERTICAL) { + panning = Vector2(0, panning.x + panning.y); + } else if (mb->is_shift_pressed()) { + panning = Vector2(panning.y, panning.x); } - callback_helper(scroll_callback, varray(panning, mb->is_alt_pressed())); + callback_helper(pan_callback, varray(-panning * scroll_speed, p_event)); return true; } else if (!mb->is_shift_pressed()) { - scroll_vec.y *= mb->get_factor(); - callback_helper(zoom_callback, varray(scroll_vec, mb->get_position(), mb->is_alt_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)); return true; } } @@ -102,14 +108,31 @@ 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))); + callback_helper(pan_callback, varray(Input::get_singleton()->warp_mouse_motion(mm, p_canvas_rect), p_event)); } else { - callback_helper(pan_callback, varray(mm->get_relative())); + callback_helper(pan_callback, varray(mm->get_relative(), p_event)); } return true; } } + 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)); + return true; + } + + Ref<InputEventPanGesture> pan_gesture = p_event; + if (pan_gesture.is_valid()) { + callback_helper(pan_callback, varray(-pan_gesture->get_delta(), p_event)); + } + + Ref<InputEventScreenDrag> screen_drag = p_event; + if (screen_drag.is_valid()) { + callback_helper(pan_callback, varray(screen_drag->get_relative(), p_event)); + } + Ref<InputEventKey> k = p_event; if (k.is_valid()) { if (pan_view_shortcut.is_valid() && pan_view_shortcut->matches_event(k)) { @@ -140,8 +163,7 @@ void ViewPanner::callback_helper(Callable p_callback, Vector<Variant> p_args) { p_callback.callp(argptr, p_args.size(), result, ce); } -void ViewPanner::set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback) { - scroll_callback = p_scroll_callback; +void ViewPanner::set_callbacks(Callable p_pan_callback, Callable p_zoom_callback) { pan_callback = p_pan_callback; zoom_callback = p_zoom_callback; } @@ -163,6 +185,20 @@ void ViewPanner::set_simple_panning_enabled(bool p_enabled) { simple_panning_enabled = p_enabled; } +void ViewPanner::set_scroll_speed(int p_scroll_speed) { + ERR_FAIL_COND(p_scroll_speed <= 0); + scroll_speed = p_scroll_speed; +} + +void ViewPanner::set_scroll_zoom_factor(float p_scroll_zoom_factor) { + ERR_FAIL_COND(p_scroll_zoom_factor <= 1.0); + scroll_zoom_factor = p_scroll_zoom_factor; +} + +void ViewPanner::set_pan_axis(PanAxis p_pan_axis) { + pan_axis = p_pan_axis; +} + void ViewPanner::setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning) { set_control_scheme(p_scheme); set_pan_shortcut(p_shortcut); diff --git a/scene/gui/view_panner.h b/scene/gui/view_panner.h index 861574a80c..60d36ca04c 100644 --- a/scene/gui/view_panner.h +++ b/scene/gui/view_panner.h @@ -45,7 +45,17 @@ public: SCROLL_PANS, }; + enum PanAxis { + PAN_AXIS_BOTH, + PAN_AXIS_HORIZONTAL, + PAN_AXIS_VERTICAL, + }; + private: + int scroll_speed = 32; + float scroll_zoom_factor = 1.1; + PanAxis pan_axis = PAN_AXIS_BOTH; + bool is_dragging = false; bool pan_key_pressed = false; bool force_drag = false; @@ -55,7 +65,6 @@ private: Ref<Shortcut> pan_view_shortcut; - Callable scroll_callback; Callable pan_callback; Callable zoom_callback; @@ -63,11 +72,14 @@ private: ControlScheme control_scheme = SCROLL_ZOOMS; public: - void set_callbacks(Callable p_scroll_callback, Callable p_pan_callback, Callable p_zoom_callback); + void set_callbacks(Callable p_pan_callback, Callable p_zoom_callback); void set_control_scheme(ControlScheme p_scheme); void set_enable_rmb(bool p_enable); void set_pan_shortcut(Ref<Shortcut> p_shortcut); void set_simple_panning_enabled(bool p_enabled); + void set_scroll_speed(int p_scroll_speed); + void set_scroll_zoom_factor(float p_scroll_zoom_factor); + void set_pan_axis(PanAxis p_pan_axis); void setup(ControlScheme p_scheme, Ref<Shortcut> p_shortcut, bool p_simple_panning); |