diff options
Diffstat (limited to 'scene/gui')
-rw-r--r-- | scene/gui/base_button.cpp | 19 | ||||
-rw-r--r-- | scene/gui/base_button.h | 5 | ||||
-rw-r--r-- | scene/gui/code_edit.cpp | 35 | ||||
-rw-r--r-- | scene/gui/code_edit.h | 1 | ||||
-rw-r--r-- | scene/gui/color_mode.cpp | 84 | ||||
-rw-r--r-- | scene/gui/color_picker.cpp | 69 | ||||
-rw-r--r-- | scene/gui/color_picker.h | 2 | ||||
-rw-r--r-- | scene/gui/control.cpp | 34 | ||||
-rw-r--r-- | scene/gui/flow_container.cpp | 41 | ||||
-rw-r--r-- | scene/gui/flow_container.h | 13 | ||||
-rw-r--r-- | scene/gui/graph_edit.cpp | 8 | ||||
-rw-r--r-- | scene/gui/item_list.cpp | 4 | ||||
-rw-r--r-- | scene/gui/line_edit.cpp | 29 | ||||
-rw-r--r-- | scene/gui/line_edit.h | 7 | ||||
-rw-r--r-- | scene/gui/rich_text_effect.cpp | 6 | ||||
-rw-r--r-- | scene/gui/rich_text_label.cpp | 32 | ||||
-rw-r--r-- | scene/gui/rich_text_label.h | 2 | ||||
-rw-r--r-- | scene/gui/scroll_container.cpp | 107 | ||||
-rw-r--r-- | scene/gui/spin_box.cpp | 17 | ||||
-rw-r--r-- | scene/gui/spin_box.h | 3 | ||||
-rw-r--r-- | scene/gui/text_edit.cpp | 215 | ||||
-rw-r--r-- | scene/gui/text_edit.h | 10 | ||||
-rw-r--r-- | scene/gui/texture_progress_bar.cpp | 26 |
23 files changed, 563 insertions, 206 deletions
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 552345e4fe..1082fc4d6d 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -127,6 +127,7 @@ void BaseButton::_notification(int p_what) { status.hovering = false; status.press_attempt = false; status.pressing_inside = false; + status.shortcut_press = false; } break; } } @@ -160,6 +161,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) { 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(); @@ -185,6 +187,7 @@ 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")); } @@ -209,6 +212,7 @@ void BaseButton::set_disabled(bool p_disabled) { } status.press_attempt = false; status.pressing_inside = false; + status.shortcut_press = false; } queue_redraw(); } @@ -284,7 +288,7 @@ BaseButton::DrawMode BaseButton::get_draw_mode() const { pressing = status.pressed; } - if (pressing) { + if ((shortcut_feedback || !status.shortcut_press) && pressing) { return DRAW_PRESSED; } else { return DRAW_NORMAL; @@ -350,6 +354,7 @@ 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); accept_event(); } @@ -389,6 +394,14 @@ 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; +} + void BaseButton::_bind_methods() { ClassDB::bind_method(D_METHOD("set_pressed", "pressed"), &BaseButton::set_pressed); ClassDB::bind_method(D_METHOD("is_pressed"), &BaseButton::is_pressed); @@ -414,6 +427,9 @@ 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"); @@ -430,6 +446,7 @@ void BaseButton::_bind_methods() { 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, "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"); BIND_ENUM_CONSTANT(DRAW_NORMAL); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index 7839239800..3acf535f54 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -53,6 +53,7 @@ private: bool keep_pressed_outside = false; Ref<Shortcut> shortcut; ObjectID shortcut_context; + bool shortcut_feedback = true; ActionMode action_mode = ACTION_MODE_BUTTON_RELEASE; struct Status { @@ -60,6 +61,7 @@ private: bool hovering = false; bool press_attempt = false; bool pressing_inside = false; + bool shortcut_press = false; bool disabled = false; @@ -131,6 +133,9 @@ 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; + BaseButton(); ~BaseButton(); }; diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp index 8a5d04f49c..5f8f25154c 100644 --- a/scene/gui/code_edit.cpp +++ b/scene/gui/code_edit.cpp @@ -138,7 +138,7 @@ void CodeEdit::_notification(int p_what) { code_completion_scroll_rect.position = code_completion_rect.position + Vector2(code_completion_rect.size.width, 0); code_completion_scroll_rect.size = Vector2(scroll_width, code_completion_rect.size.height); - code_completion_line_ofs = CLAMP(code_completion_current_selected - lines / 2, 0, code_completion_options_count - lines); + code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines); RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), code_completion_selected_color); for (int i = 0; i < lines; i++) { @@ -281,16 +281,22 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { case MouseButton::WHEEL_UP: { if (code_completion_current_selected > 0) { code_completion_current_selected--; + code_completion_force_item_center = -1; queue_redraw(); } } break; case MouseButton::WHEEL_DOWN: { if (code_completion_current_selected < code_completion_options.size() - 1) { code_completion_current_selected++; + code_completion_force_item_center = -1; queue_redraw(); } } break; case MouseButton::LEFT: { + if (code_completion_force_item_center == -1) { + code_completion_force_item_center = code_completion_current_selected; + } + code_completion_current_selected = CLAMP(code_completion_line_ofs + (mb->get_position().y - code_completion_rect.position.y) / get_line_height(), 0, code_completion_options.size() - 1); if (mb->is_double_click()) { confirm_code_completion(); @@ -300,6 +306,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { default: break; } + return; } else if (code_completion_active && code_completion_scroll_rect.has_point(mb->get_position())) { if (mb->get_button_index() != MouseButton::LEFT) { @@ -448,6 +455,7 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } else { code_completion_current_selected = code_completion_options.size() - 1; } + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; @@ -458,30 +466,35 @@ void CodeEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } else { code_completion_current_selected = 0; } + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; } if (k->is_action("ui_page_up", true)) { code_completion_current_selected = MAX(0, code_completion_current_selected - code_completion_max_lines); + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; } if (k->is_action("ui_page_down", true)) { code_completion_current_selected = MIN(code_completion_options.size() - 1, code_completion_current_selected + code_completion_max_lines); + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; } if (k->is_action("ui_home", true)) { code_completion_current_selected = 0; + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; } if (k->is_action("ui_end", true)) { code_completion_current_selected = code_completion_options.size() - 1; + code_completion_force_item_center = -1; queue_redraw(); accept_event(); return; @@ -1978,6 +1991,7 @@ void CodeEdit::set_code_completion_selected_index(int p_index) { } ERR_FAIL_INDEX(p_index, code_completion_options.size()); code_completion_current_selected = p_index; + code_completion_force_item_center = -1; queue_redraw(); } @@ -2808,6 +2822,7 @@ void CodeEdit::_update_scroll_selected_line(float p_mouse_y) { percent = CLAMP(percent, 0.0f, 1.0f); code_completion_current_selected = (int)(percent * (code_completion_options.size() - 1)); + code_completion_force_item_center = -1; } void CodeEdit::_filter_code_completion_candidates_impl() { @@ -2859,12 +2874,15 @@ void CodeEdit::_filter_code_completion_candidates_impl() { offset = line_height; } - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (font.is_valid()) { + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + } code_completion_options.push_back(option); } code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); code_completion_current_selected = 0; + code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); return; @@ -2970,7 +2988,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() { if (string_to_complete.length() == 0) { code_completion_options.push_back(option); - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (font.is_valid()) { + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + } continue; } @@ -3076,7 +3096,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option.matches.append_array(ssq_matches); completion_options_subseq.push_back(option); } - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (font.is_valid()) { + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + } } else if (!*ssq_lower) { // Matched the whole subsequence in s_lower. option.matches.clear(); @@ -3093,7 +3115,9 @@ void CodeEdit::_filter_code_completion_candidates_impl() { option.matches.append_array(ssq_lower_matches); completion_options_subseq_casei.push_back(option); } - max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + if (font.is_valid()) { + max_width = MAX(max_width, font->get_string_size(option.display, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).width + offset); + } } } @@ -3115,6 +3139,7 @@ void CodeEdit::_filter_code_completion_candidates_impl() { code_completion_longest_line = MIN(max_width, code_completion_max_width * font_size); code_completion_current_selected = 0; + code_completion_force_item_center = -1; code_completion_active = true; queue_redraw(); } diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h index 09c7ef80bf..cbbc13480e 100644 --- a/scene/gui/code_edit.h +++ b/scene/gui/code_edit.h @@ -214,6 +214,7 @@ private: Vector<ScriptLanguage::CodeCompletionOption> code_completion_options; int code_completion_line_ofs = 0; int code_completion_current_selected = 0; + int code_completion_force_item_center = -1; int code_completion_longest_line = 0; Rect2i code_completion_rect; Rect2i code_completion_scroll_rect; diff --git a/scene/gui/color_mode.cpp b/scene/gui/color_mode.cpp index 3a5013dabe..a063cd344a 100644 --- a/scene/gui/color_mode.cpp +++ b/scene/gui/color_mode.cpp @@ -284,46 +284,68 @@ Color ColorModeOKHSL::get_color() const { } void ColorModeOKHSL::slider_draw(int p_which) { - Vector<Vector2> pos; - pos.resize(4); - Vector<Color> col; - col.resize(4); HSlider *slider = color_picker->get_slider(p_which); Size2 size = slider->get_size(); - Color left_color; - Color right_color; - Color color = color_picker->get_pick_color(); const real_t margin = 16 * color_picker->get_theme_default_base_scale(); - if (p_which == ColorPicker::SLIDER_COUNT) { - slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true); - - left_color = color; - left_color.a = 0; - right_color = color; - right_color.a = 1; - } else if (p_which == 0) { + if (p_which == 0) { // H Ref<Texture2D> hue = color_picker->get_theme_icon(SNAME("color_hue"), SNAME("ColorPicker")); slider->draw_set_transform(Point2(), -Math_PI / 2, Size2(1.0, 1.0)); slider->draw_texture_rect(hue, Rect2(Vector2(margin * -1, 0), Vector2(margin, size.x)), false); return; - } else { - Color s_col; - Color v_col; - s_col.set_ok_hsl(color.get_h(), 0, color.get_v()); - left_color = (p_which == 1) ? s_col : Color(0, 0, 0); - s_col.set_ok_hsl(color.get_h(), 1, color.get_v()); - v_col.set_ok_hsl(color.get_h(), color.get_s(), 1); - right_color = (p_which == 1) ? s_col : v_col; } - col.set(0, left_color); - col.set(1, right_color); - col.set(2, right_color); - col.set(3, left_color); - pos.set(0, Vector2(0, 0)); - pos.set(1, Vector2(size.x, 0)); - pos.set(2, Vector2(size.x, margin)); - pos.set(3, Vector2(0, margin)); + + Vector<Vector2> pos; + Vector<Color> col; + Color left_color; + Color right_color; + Color color = color_picker->get_pick_color(); + + if (p_which == 2) { // L + pos.resize(6); + col.resize(6); + left_color = Color(0, 0, 0); + Color middle_color; + middle_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 0.5); + right_color.set_ok_hsl(color.get_ok_hsl_h(), color.get_ok_hsl_s(), 1); + + col.set(0, left_color); + col.set(1, middle_color); + col.set(2, right_color); + col.set(3, right_color); + col.set(4, middle_color); + col.set(5, left_color); + pos.set(0, Vector2(0, 0)); + pos.set(1, Vector2(size.x * 0.5, 0)); + pos.set(2, Vector2(size.x, 0)); + pos.set(3, Vector2(size.x, margin)); + pos.set(4, Vector2(size.x * 0.5, margin)); + pos.set(5, Vector2(0, margin)); + } else { // A / S + pos.resize(4); + col.resize(4); + + if (p_which == ColorPicker::SLIDER_COUNT) { + slider->draw_texture_rect(color_picker->get_theme_icon(SNAME("sample_bg"), SNAME("ColorPicker")), Rect2(Point2(0, 0), Size2(size.x, margin)), true); + + left_color = color; + left_color.a = 0; + right_color = color; + right_color.a = 1; + } else { + left_color.set_ok_hsl(color.get_ok_hsl_h(), 0, color.get_ok_hsl_l()); + right_color.set_ok_hsl(color.get_ok_hsl_h(), 1, color.get_ok_hsl_l()); + } + + col.set(0, left_color); + col.set(1, right_color); + col.set(2, right_color); + col.set(3, left_color); + pos.set(0, Vector2(0, 0)); + pos.set(1, Vector2(size.x, 0)); + pos.set(2, Vector2(size.x, margin)); + pos.set(3, Vector2(0, margin)); + } slider->draw_polygon(pos, col); } diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 929bf27be6..1b87c1d709 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -368,11 +368,10 @@ void ColorPicker::create_slider(GridContainer *gc, int idx) { SpinBox *val = memnew(SpinBox); slider->share(val); + val->set_select_all_on_focus(true); gc->add_child(val); LineEdit *vle = val->get_line_edit(); - vle->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter), CONNECT_DEFERRED); - vle->connect("focus_exited", callable_mp(this, &ColorPicker::_focus_exit)); vle->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed)); vle->connect("gui_input", callable_mp(this, &ColorPicker::_line_edit_input)); vle->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT); @@ -744,7 +743,7 @@ void ColorPicker::add_recent_preset(const Color &p_color) { if (recent_preset_hbc->get_child_count() >= PRESET_COLUMN_COUNT) { recent_preset_cache.pop_front(); recent_presets.pop_front(); - recent_preset_hbc->get_child(PRESET_COLUMN_COUNT - 1)->queue_delete(); + recent_preset_hbc->get_child(PRESET_COLUMN_COUNT - 1)->queue_free(); } recent_presets.push_back(p_color); recent_preset_cache.push_back(p_color); @@ -770,7 +769,7 @@ void ColorPicker::erase_preset(const Color &p_color) { for (int i = 1; i < preset_container->get_child_count(); i++) { ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(preset_container->get_child(i)); if (current_btn && p_color == current_btn->get_preset_color()) { - current_btn->queue_delete(); + current_btn->queue_free(); break; } } @@ -794,7 +793,7 @@ void ColorPicker::erase_recent_preset(const Color &p_color) { for (int i = 1; i < recent_preset_hbc->get_child_count(); i++) { ColorPresetButton *current_btn = Object::cast_to<ColorPresetButton>(recent_preset_hbc->get_child(i)); if (current_btn && p_color == current_btn->get_preset_color()) { - current_btn->queue_delete(); + current_btn->queue_free(); break; } } @@ -1087,16 +1086,24 @@ void ColorPicker::_hsv_draw(int p_which, Control *c) { Vector<Color> colors; Color col; col.set_ok_hsl(h, s, 1); - points.resize(4); - colors.resize(4); - points.set(0, Vector2()); - points.set(1, Vector2(c->get_size().x, 0)); + Color col2; + col2.set_ok_hsl(h, s, 0.5); + Color col3; + col3.set_ok_hsl(h, s, 0); + points.resize(6); + colors.resize(6); + points.set(0, Vector2(c->get_size().x, 0)); + points.set(1, Vector2(c->get_size().x, c->get_size().y * 0.5)); points.set(2, c->get_size()); points.set(3, Vector2(0, c->get_size().y)); + points.set(4, Vector2(0, c->get_size().y * 0.5)); + points.set(5, Vector2()); colors.set(0, col); - colors.set(1, col); - colors.set(2, Color(0, 0, 0)); - colors.set(3, Color(0, 0, 0)); + colors.set(1, col2); + colors.set(2, col3); + colors.set(3, col3); + colors.set(4, col2); + colors.set(5, col); c->draw_polygon(points, colors); int y = c->get_size().y - c->get_size().y * CLAMP(v, 0, 1); col.set_ok_hsl(h, 1, v); @@ -1407,47 +1414,11 @@ void ColorPicker::_screen_pick_pressed() { //screen->show_modal(); } -void ColorPicker::_focus_enter() { - bool has_ctext_focus = c_text->has_focus(); - if (has_ctext_focus) { - c_text->select_all(); - } else { - c_text->select(0, 0); - } - - for (int i = 0; i < current_slider_count; i++) { - if (values[i]->get_line_edit()->has_focus() && !has_ctext_focus) { - values[i]->get_line_edit()->select_all(); - } else { - values[i]->get_line_edit()->select(0, 0); - } - } - if (alpha_value->get_line_edit()->has_focus() && !has_ctext_focus) { - alpha_value->get_line_edit()->select_all(); - } else { - alpha_value->get_line_edit()->select(0, 0); - } -} - -void ColorPicker::_focus_exit() { - for (int i = 0; i < current_slider_count; i++) { - if (!values[i]->get_line_edit()->get_menu()->is_visible()) { - values[i]->get_line_edit()->select(0, 0); - } - } - if (!alpha_value->get_line_edit()->get_menu()->is_visible()) { - alpha_value->get_line_edit()->select(0, 0); - } - - c_text->select(0, 0); -} - void ColorPicker::_html_focus_exit() { if (c_text->is_menu_visible()) { return; } _html_submitted(c_text->get_text()); - _focus_exit(); } void ColorPicker::set_presets_enabled(bool p_enabled) { @@ -1658,9 +1629,9 @@ ColorPicker::ColorPicker() : c_text = memnew(LineEdit); hhb->add_child(c_text); + c_text->set_select_all_on_focus(true); c_text->connect("text_submitted", callable_mp(this, &ColorPicker::_html_submitted)); c_text->connect("text_changed", callable_mp(this, &ColorPicker::_text_changed)); - c_text->connect("focus_entered", callable_mp(this, &ColorPicker::_focus_enter), CONNECT_DEFERRED); c_text->connect("focus_exited", callable_mp(this, &ColorPicker::_html_focus_exit)); wheel_edit = memnew(AspectRatioContainer); diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index 6c91575893..a0843d6fa2 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -202,8 +202,6 @@ private: void _text_changed(const String &p_new_text); void _add_preset_pressed(); void _screen_pick_pressed(); - void _focus_enter(); - void _focus_exit(); void _html_focus_exit(); inline int _get_preset_size(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 2ec0a48278..c5cb7157e8 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -633,7 +633,7 @@ Rect2 Control::get_parent_anchorable_rect() const { #ifdef TOOLS_ENABLED Node *edited_root = get_tree()->get_edited_scene_root(); if (edited_root && (this == edited_root || edited_root->is_ancestor_of(this))) { - parent_rect.size = Size2(ProjectSettings::get_singleton()->get("display/window/size/viewport_width"), ProjectSettings::get_singleton()->get("display/window/size/viewport_height")); + parent_rect.size = Size2(GLOBAL_GET("display/window/size/viewport_width"), GLOBAL_GET("display/window/size/viewport_height")); } else { parent_rect = get_viewport()->get_visible_rect(); } @@ -1551,10 +1551,8 @@ bool Control::is_minimum_size_adjust_blocked() const { Size2 Control::get_minimum_size() const { Vector2 ms; - if (GDVIRTUAL_CALL(_get_minimum_size, ms)) { - return ms; - } - return Vector2(); + GDVIRTUAL_CALL(_get_minimum_size, ms); + return ms; } void Control::set_custom_minimum_size(const Size2 &p_custom) { @@ -1799,11 +1797,8 @@ Variant Control::get_drag_data(const Point2 &p_point) { } Variant dd; - if (GDVIRTUAL_CALL(_get_drag_data, p_point, dd)) { - return dd; - } - - return Variant(); + GDVIRTUAL_CALL(_get_drag_data, p_point, dd); + return dd; } bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const { @@ -1815,10 +1810,8 @@ bool Control::can_drop_data(const Point2 &p_point, const Variant &p_data) const } bool ret = false; - if (GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret)) { - return ret; - } - return false; + GDVIRTUAL_CALL(_can_drop_data, p_point, p_data, ret); + return ret; } void Control::drop_data(const Point2 &p_point, const Variant &p_data) { @@ -2725,11 +2718,8 @@ void Control::end_bulk_theme_override() { TypedArray<Vector2i> 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; - if (GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret)) { - return ret; - } else { - return TypedArray<Vector2i>(); - } + GDVIRTUAL_CALL(_structured_text_parser, p_args, p_text, ret); + return ret; } else { return TS->parse_structured_text(p_parser_type, p_args, p_text); } @@ -2814,10 +2804,8 @@ String Control::get_tooltip(const Point2 &p_pos) const { Control *Control::make_custom_tooltip(const String &p_text) const { Object *ret = nullptr; - if (GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret)) { - return Object::cast_to<Control>(ret); - } - return nullptr; + GDVIRTUAL_CALL(_make_custom_tooltip, p_text, ret); + return Object::cast_to<Control>(ret); } // Base object overrides. diff --git a/scene/gui/flow_container.cpp b/scene/gui/flow_container.cpp index b0d15aa7f4..44c5ec62f8 100644 --- a/scene/gui/flow_container.cpp +++ b/scene/gui/flow_container.cpp @@ -152,6 +152,28 @@ void FlowContainer::_resort() { line_data = lines_data[current_line_idx]; } + // The first child of each line adds the offset caused by the alignment, + // but only if the line doesn't contain a child that expands. + if (child_idx_in_line == 0 && Math::is_equal_approx(line_data.stretch_ratio_total, 0)) { + int alignment_ofs = 0; + switch (alignment) { + case ALIGNMENT_CENTER: + alignment_ofs = line_data.stretch_avail / 2; + break; + case ALIGNMENT_END: + alignment_ofs = line_data.stretch_avail; + break; + default: + break; + } + + if (vertical) { /* VERTICAL */ + ofs.y += alignment_ofs; + } else { /* HORIZONTAL */ + ofs.x += alignment_ofs; + } + } + if (vertical) { /* VERTICAL */ if (child->get_h_size_flags() & (SIZE_FILL | SIZE_SHRINK_CENTER | SIZE_SHRINK_END)) { child_size.width = line_data.min_line_height; @@ -282,6 +304,18 @@ int FlowContainer::get_line_count() const { return cached_line_count; } +void FlowContainer::set_alignment(AlignmentMode p_alignment) { + if (alignment == p_alignment) { + return; + } + alignment = p_alignment; + _resort(); +} + +FlowContainer::AlignmentMode FlowContainer::get_alignment() const { + return alignment; +} + void FlowContainer::set_vertical(bool p_vertical) { ERR_FAIL_COND_MSG(is_fixed, "Can't change orientation of " + get_class() + "."); vertical = p_vertical; @@ -300,8 +334,15 @@ FlowContainer::FlowContainer(bool p_vertical) { void FlowContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_line_count"), &FlowContainer::get_line_count); + ClassDB::bind_method(D_METHOD("set_alignment", "alignment"), &FlowContainer::set_alignment); + ClassDB::bind_method(D_METHOD("get_alignment"), &FlowContainer::get_alignment); ClassDB::bind_method(D_METHOD("set_vertical", "vertical"), &FlowContainer::set_vertical); ClassDB::bind_method(D_METHOD("is_vertical"), &FlowContainer::is_vertical); + BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN); + BIND_ENUM_CONSTANT(ALIGNMENT_CENTER); + BIND_ENUM_CONSTANT(ALIGNMENT_END); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment", "get_alignment"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "vertical"), "set_vertical", "is_vertical"); } diff --git a/scene/gui/flow_container.h b/scene/gui/flow_container.h index 536df27ad6..6a61e9b904 100644 --- a/scene/gui/flow_container.h +++ b/scene/gui/flow_container.h @@ -36,11 +36,19 @@ class FlowContainer : public Container { GDCLASS(FlowContainer, Container); +public: + enum AlignmentMode { + ALIGNMENT_BEGIN, + ALIGNMENT_CENTER, + ALIGNMENT_END + }; + private: int cached_size = 0; int cached_line_count = 0; bool vertical = false; + AlignmentMode alignment = ALIGNMENT_BEGIN; struct ThemeCache { int h_separation = 0; @@ -61,6 +69,9 @@ protected: public: int get_line_count() const; + void set_alignment(AlignmentMode p_alignment); + AlignmentMode get_alignment() const; + void set_vertical(bool p_vertical); bool is_vertical() const; @@ -88,4 +99,6 @@ public: FlowContainer(true) { is_fixed = true; } }; +VARIANT_ENUM_CAST(FlowContainer::AlignmentMode); + #endif // FLOW_CONTAINER_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index caf8db4515..46b712379d 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1475,11 +1475,9 @@ void GraphEdit::force_connection_drag_end() { } bool GraphEdit::is_node_hover_valid(const StringName &p_from, const int p_from_port, const StringName &p_to, const int p_to_port) { - bool valid = false; - if (GDVIRTUAL_CALL(_is_node_hover_valid, p_from, p_from_port, p_to, p_to_port, valid)) { - return valid; - } - return true; + bool valid = true; + GDVIRTUAL_CALL(_is_node_hover_valid, p_from, p_from_port, p_to, p_to_port, valid); + return valid; } void GraphEdit::set_panning_scheme(PanningScheme p_scheme) { diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 223428906a..d6b5557a3f 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -733,7 +733,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; - if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) { + if (diff < uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec")) * 2) { for (int i = current - 1; i >= 0; i--) { if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) { set_current(i); @@ -771,7 +771,7 @@ void ItemList::gui_input(const Ref<InputEvent> &p_event) { uint64_t now = OS::get_singleton()->get_ticks_msec(); uint64_t diff = now - search_time_msec; - if (diff < uint64_t(ProjectSettings::get_singleton()->get("gui/timers/incremental_search_max_interval_msec")) * 2) { + if (diff < uint64_t(GLOBAL_GET("gui/timers/incremental_search_max_interval_msec")) * 2) { for (int i = current + 1; i < items.size(); i++) { if (CAN_SELECT(i) && items[i].text.begins_with(search_string)) { set_current(i); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 65670b7786..fb5ab9f923 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -372,6 +372,11 @@ void LineEdit::gui_input(const Ref<InputEvent> &p_event) { selection.drag_attempt = false; } + if (pending_select_all_on_focus) { + select_all(); + pending_select_all_on_focus = false; + } + show_virtual_keyboard(); } @@ -1056,6 +1061,15 @@ void LineEdit::_notification(int p_what) { } } + if (select_all_on_focus) { + if (Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { + // Select all when the mouse button is up. + pending_select_all_on_focus = true; + } else { + select_all(); + } + } + 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; @@ -2164,6 +2178,18 @@ bool LineEdit::is_flat() const { return flat; } +void LineEdit::set_select_all_on_focus(bool p_enabled) { + select_all_on_focus = p_enabled; +} + +bool LineEdit::is_select_all_on_focus() const { + return select_all_on_focus; +} + +void LineEdit::clear_pending_select_all_on_focus() { + pending_select_all_on_focus = false; +} + void LineEdit::_text_changed() { _emit_text_change(); _clear_redo(); @@ -2367,6 +2393,8 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &LineEdit::set_flat); ClassDB::bind_method(D_METHOD("is_flat"), &LineEdit::is_flat); + ClassDB::bind_method(D_METHOD("set_select_all_on_focus", "enabled"), &LineEdit::set_select_all_on_focus); + ClassDB::bind_method(D_METHOD("is_select_all_on_focus"), &LineEdit::is_select_all_on_focus); ADD_SIGNAL(MethodInfo("text_changed", PropertyInfo(Variant::STRING, "new_text"))); ADD_SIGNAL(MethodInfo("text_change_rejected", PropertyInfo(Variant::STRING, "rejected_substring"))); @@ -2430,6 +2458,7 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); ADD_GROUP("Caret", "caret_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "caret_blink"), "set_caret_blink_enabled", "is_caret_blink_enabled"); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 861c7f273b..e0a079b623 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -174,6 +174,9 @@ private: double caret_blink_timer = 0.0; bool caret_blinking = false; + bool pending_select_all_on_focus = false; + bool select_all_on_focus = false; + struct ThemeCache { Ref<StyleBox> normal; Ref<StyleBox> read_only; @@ -365,6 +368,10 @@ public: void set_flat(bool p_enabled); bool is_flat() const; + void set_select_all_on_focus(bool p_enabled); + bool is_select_all_on_focus() const; + void clear_pending_select_all_on_focus(); // For other controls, e.g. SpinBox. + virtual bool is_text_field() const override; void show_virtual_keyboard(); diff --git a/scene/gui/rich_text_effect.cpp b/scene/gui/rich_text_effect.cpp index c9516ed6b9..0dece1c287 100644 --- a/scene/gui/rich_text_effect.cpp +++ b/scene/gui/rich_text_effect.cpp @@ -56,10 +56,8 @@ Variant RichTextEffect::get_bbcode() const { bool RichTextEffect::_process_effect_impl(Ref<CharFXTransform> p_cfx) { bool return_value = false; - if (GDVIRTUAL_CALL(_process_custom_fx, p_cfx, return_value)) { - return return_value; - } - return false; + GDVIRTUAL_CALL(_process_custom_fx, p_cfx, return_value); + return return_value; } RichTextEffect::RichTextEffect() { diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index c96d3c763d..7f487175dc 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -1087,7 +1087,6 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o _draw_fbg_boxes(ci, rid, fbg_line_off, it_from, it_to, chr_range.x, chr_range.y, 0); // Draw main text. - Color selection_fg = theme_cache.font_selected_color; Color selection_bg = theme_cache.selection_color; int sel_start = -1; @@ -1276,8 +1275,8 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o } } - if (selected) { - font_color = override_selected_font_color ? selection_fg : font_color; + if (selected && use_selected_font_color) { + font_color = theme_cache.font_selected_color; } // Draw glyphs. @@ -1286,9 +1285,9 @@ int RichTextLabel::_draw_line(ItemFrame *p_frame, int p_line, const Vector2 &p_o if (txt_visible) { if (!skip) { if (frid != RID()) { - TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + TS->font_draw_glyph(frid, ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, font_color); } else if ((glyphs[i].flags & TextServer::GRAPHEME_IS_VIRTUAL) != TextServer::GRAPHEME_IS_VIRTUAL) { - TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, selected ? selection_fg : font_color); + TS->draw_hex_code_box(ci, glyphs[i].font_size, p_ofs + fx_offset + off, gl, font_color); } } r_processed_glyphs++; @@ -1688,6 +1687,7 @@ void RichTextLabel::_update_theme_item_cache() { theme_cache.default_color = get_theme_color(SNAME("default_color")); theme_cache.font_selected_color = get_theme_color(SNAME("font_selected_color")); + use_selected_font_color = theme_cache.font_selected_color != Color(0, 0, 0, 0); theme_cache.selection_color = get_theme_color(SNAME("selection_color")); theme_cache.font_outline_color = get_theme_color(SNAME("font_outline_color")); theme_cache.font_shadow_color = get_theme_color(SNAME("font_shadow_color")); @@ -3564,14 +3564,6 @@ bool RichTextLabel::is_hint_underlined() const { return underline_hint; } -void RichTextLabel::set_override_selected_font_color(bool p_override_selected_font_color) { - override_selected_font_color = p_override_selected_font_color; -} - -bool RichTextLabel::is_overriding_selected_font_color() const { - return override_selected_font_color; -} - void RichTextLabel::set_offset(int p_pixel) { vscroll->set_value(p_pixel); } @@ -5294,9 +5286,6 @@ void RichTextLabel::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hint_underline", "enable"), &RichTextLabel::set_hint_underline); ClassDB::bind_method(D_METHOD("is_hint_underlined"), &RichTextLabel::is_hint_underlined); - ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &RichTextLabel::set_override_selected_font_color); - ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &RichTextLabel::is_overriding_selected_font_color); - ClassDB::bind_method(D_METHOD("set_scroll_active", "active"), &RichTextLabel::set_scroll_active); ClassDB::bind_method(D_METHOD("is_scroll_active"), &RichTextLabel::is_scroll_active); @@ -5406,7 +5395,6 @@ void RichTextLabel::_bind_methods() { ADD_GROUP("Text Selection", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selection_enabled"), "set_selection_enabled", "is_selection_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_GROUP("Displayed Text", ""); @@ -5655,6 +5643,8 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item Vector2i fbg_index = Vector2i(end, start); Color last_color = Color(0, 0, 0, 0); bool draw_box = false; + int hpad = get_theme_constant(SNAME("text_highlight_h_padding")); + int vpad = get_theme_constant(SNAME("text_highlight_v_padding")); // Draw a box based on color tags associated with glyphs for (int i = start; i < end; i++) { Item *it = _get_item_at_pos(it_from, it_to, i); @@ -5684,8 +5674,8 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item if (draw_box) { Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, fbg_index.y); for (int j = 0; j < sel.size(); j++) { - Vector2 rect_off = line_off + Vector2(sel[j].x, -TS->shaped_text_get_ascent(p_rid)); - Vector2 rect_size = Vector2(sel[j].y - sel[j].x, TS->shaped_text_get_size(p_rid).y); + Vector2 rect_off = line_off + Vector2(sel[j].x - hpad, -TS->shaped_text_get_ascent(p_rid) - vpad); + Vector2 rect_size = Vector2(sel[j].y - sel[j].x + 2 * hpad, TS->shaped_text_get_size(p_rid).y + 2 * vpad); RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color); } fbg_index = Vector2i(end, start); @@ -5703,8 +5693,8 @@ void RichTextLabel::_draw_fbg_boxes(RID p_ci, RID p_rid, Vector2 line_off, Item if (last_color.a > 0) { Vector<Vector2> sel = TS->shaped_text_get_selection(p_rid, fbg_index.x, end); for (int i = 0; i < sel.size(); i++) { - Vector2 rect_off = line_off + Vector2(sel[i].x, -TS->shaped_text_get_ascent(p_rid)); - Vector2 rect_size = Vector2(sel[i].y - sel[i].x, TS->shaped_text_get_size(p_rid).y); + Vector2 rect_off = line_off + Vector2(sel[i].x - hpad, -TS->shaped_text_get_ascent(p_rid) - vpad); + Vector2 rect_size = Vector2(sel[i].y - sel[i].x + 2 * hpad, TS->shaped_text_get_size(p_rid).y + 2 * vpad); RenderingServer::get_singleton()->canvas_item_add_rect(p_ci, Rect2(rect_off, rect_size), last_color); } } diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 04a682349d..d30baaa8d3 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -396,7 +396,7 @@ private: int tab_size = 4; bool underline_meta = true; bool underline_hint = true; - bool override_selected_font_color = false; + bool use_selected_font_color = false; HorizontalAlignment default_alignment = HORIZONTAL_ALIGNMENT_LEFT; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index a6aa4707ff..ec34a8d26d 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -107,45 +107,65 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { double prev_v_scroll = v_scroll->get_value(); double prev_h_scroll = h_scroll->get_value(); + bool h_scroll_enabled = horizontal_scroll_mode != SCROLL_MODE_DISABLED; + bool v_scroll_enabled = vertical_scroll_mode != SCROLL_MODE_DISABLED; Ref<InputEventMouseButton> mb = p_gui_input; if (mb.is_valid()) { - if (mb->get_button_index() == MouseButton::WHEEL_UP && mb->is_pressed()) { - // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) { - h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() / 8 * mb->get_factor()); - } else if (v_scroll->is_visible_in_tree()) { - v_scroll->set_value(v_scroll->get_value() - v_scroll->get_page() / 8 * mb->get_factor()); + if (mb->is_pressed()) { + bool scroll_value_modified = false; + + bool v_scroll_hidden = !v_scroll->is_visible() && vertical_scroll_mode != SCROLL_MODE_SHOW_NEVER; + if (mb->get_button_index() == MouseButton::WHEEL_UP) { + // By default, the vertical orientation takes precedence. This is an exception. + if ((h_scroll_enabled && mb->is_shift_pressed()) || v_scroll_hidden) { + h_scroll->set_value(prev_h_scroll - h_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } else if (v_scroll_enabled) { + v_scroll->set_value(prev_v_scroll - v_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } } - } - - if (mb->get_button_index() == MouseButton::WHEEL_DOWN && mb->is_pressed()) { - // only horizontal is enabled, scroll horizontally - if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->is_shift_pressed())) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() / 8 * mb->get_factor()); - } else if (v_scroll->is_visible()) { - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() / 8 * mb->get_factor()); + if (mb->get_button_index() == MouseButton::WHEEL_DOWN) { + if ((h_scroll_enabled && mb->is_shift_pressed()) || v_scroll_hidden) { + h_scroll->set_value(prev_h_scroll + h_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } else if (v_scroll_enabled) { + v_scroll->set_value(prev_v_scroll + v_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } } - } - if (mb->get_button_index() == MouseButton::WHEEL_LEFT && mb->is_pressed()) { - if (h_scroll->is_visible_in_tree()) { - h_scroll->set_value(h_scroll->get_value() - h_scroll->get_page() * mb->get_factor() / 8); + bool h_scroll_hidden = !h_scroll->is_visible() && horizontal_scroll_mode != SCROLL_MODE_SHOW_NEVER; + if (mb->get_button_index() == MouseButton::WHEEL_LEFT) { + // By default, the horizontal orientation takes precedence. This is an exception. + if ((v_scroll_enabled && mb->is_shift_pressed()) || h_scroll_hidden) { + v_scroll->set_value(prev_v_scroll - v_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } else if (h_scroll_enabled) { + h_scroll->set_value(prev_h_scroll - h_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } } - } - - if (mb->get_button_index() == MouseButton::WHEEL_RIGHT && mb->is_pressed()) { - if (h_scroll->is_visible_in_tree()) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * mb->get_factor() / 8); + if (mb->get_button_index() == MouseButton::WHEEL_RIGHT) { + if ((v_scroll_enabled && mb->is_shift_pressed()) || h_scroll_hidden) { + v_scroll->set_value(prev_v_scroll + v_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } else if (h_scroll_enabled) { + h_scroll->set_value(prev_h_scroll + h_scroll->get_page() / 8 * mb->get_factor()); + scroll_value_modified = true; + } } - } - if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { - accept_event(); //accept event if scroll changed + if (scroll_value_modified && (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)) { + accept_event(); // Accept event if scroll changed. + return; + } } - if (!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()))) { + bool screen_is_touchscreen = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id())); + if (!screen_is_touchscreen) { return; } @@ -161,8 +181,8 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { drag_speed = Vector2(); drag_accum = Vector2(); last_drag_accum = Vector2(); - drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value()); - drag_touching = DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id())); + drag_from = Vector2(prev_h_scroll, prev_v_scroll); + drag_touching = screen_is_touchscreen; drag_touching_deaccel = false; beyond_deadzone = false; time_since_motion = 0; @@ -180,6 +200,7 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { } } } + return; } Ref<InputEventMouseMotion> mm = p_gui_input; @@ -189,22 +210,22 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { Vector2 motion = mm->get_relative(); drag_accum -= motion; - if (beyond_deadzone || (horizontal_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.x) > deadzone) || (vertical_scroll_mode != SCROLL_MODE_DISABLED && Math::abs(drag_accum.y) > deadzone)) { + if (beyond_deadzone || (h_scroll_enabled && Math::abs(drag_accum.x) > deadzone) || (v_scroll_enabled && Math::abs(drag_accum.y) > deadzone)) { if (!beyond_deadzone) { propagate_notification(NOTIFICATION_SCROLL_BEGIN); emit_signal(SNAME("scroll_started")); beyond_deadzone = true; - // resetting drag_accum here ensures smooth scrolling after reaching deadzone + // Resetting drag_accum here ensures smooth scrolling after reaching deadzone. drag_accum = -motion; } Vector2 diff = drag_from + drag_accum; - if (horizontal_scroll_mode != SCROLL_MODE_DISABLED) { + if (h_scroll_enabled) { h_scroll->set_value(diff.x); } else { drag_accum.x = 0; } - if (vertical_scroll_mode != SCROLL_MODE_DISABLED) { + if (v_scroll_enabled) { v_scroll->set_value(diff.y); } else { drag_accum.y = 0; @@ -212,20 +233,26 @@ void ScrollContainer::gui_input(const Ref<InputEvent> &p_gui_input) { time_since_motion = 0; } } + + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { + accept_event(); // Accept event if scroll changed. + } + return; } Ref<InputEventPanGesture> pan_gesture = p_gui_input; if (pan_gesture.is_valid()) { - if (h_scroll->is_visible_in_tree()) { - h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8); + if (h_scroll_enabled) { + h_scroll->set_value(prev_h_scroll + h_scroll->get_page() * pan_gesture->get_delta().x / 8); } - if (v_scroll->is_visible_in_tree()) { - v_scroll->set_value(v_scroll->get_value() + v_scroll->get_page() * pan_gesture->get_delta().y / 8); + if (v_scroll_enabled) { + v_scroll->set_value(prev_v_scroll + v_scroll->get_page() * pan_gesture->get_delta().y / 8); } - } - if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { - accept_event(); //accept event if scroll changed + if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) { + accept_event(); // Accept event if scroll changed. + } + return; } } diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index fe14049d93..c4000120c8 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -168,6 +168,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) { range_click_timer->stop(); _release_mouse(); drag.allowed = false; + line_edit->clear_pending_select_all_on_focus(); } Ref<InputEventMouseMotion> mm = p_event; @@ -190,6 +191,11 @@ void SpinBox::_line_edit_focus_enter() { int col = line_edit->get_caret_column(); _value_changed(0); // Update the LineEdit's text. line_edit->set_caret_column(col); + + // LineEdit text might change and it clears any selection. Have to re-select here. + if (line_edit->is_select_all_on_focus() && !Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT)) { + line_edit->select_all(); + } } void SpinBox::_line_edit_focus_exit() { @@ -308,6 +314,14 @@ bool SpinBox::get_update_on_text_changed() const { return update_on_text_changed; } +void SpinBox::set_select_all_on_focus(bool p_enabled) { + line_edit->set_select_all_on_focus(p_enabled); +} + +bool SpinBox::is_select_all_on_focus() const { + return line_edit->is_select_all_on_focus(); +} + void SpinBox::set_editable(bool p_enabled) { line_edit->set_editable(p_enabled); } @@ -341,6 +355,8 @@ void SpinBox::_bind_methods() { ClassDB::bind_method(D_METHOD("is_editable"), &SpinBox::is_editable); ClassDB::bind_method(D_METHOD("set_update_on_text_changed", "enabled"), &SpinBox::set_update_on_text_changed); ClassDB::bind_method(D_METHOD("get_update_on_text_changed"), &SpinBox::get_update_on_text_changed); + ClassDB::bind_method(D_METHOD("set_select_all_on_focus", "enabled"), &SpinBox::set_select_all_on_focus); + ClassDB::bind_method(D_METHOD("is_select_all_on_focus"), &SpinBox::is_select_all_on_focus); ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply); ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit); @@ -350,6 +366,7 @@ void SpinBox::_bind_methods() { 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::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); } SpinBox::SpinBox() { diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index c2f2ac3f5a..00f5a95699 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -101,6 +101,9 @@ public: void set_update_on_text_changed(bool p_enabled); bool get_update_on_text_changed() const; + void set_select_all_on_focus(bool p_enabled); + bool is_select_all_on_focus() const; + void apply(); void set_custom_arrow_step(const double p_custom_arrow_step); double get_custom_arrow_step() const; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index b1c2676bc1..1b8444abf4 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -833,7 +833,7 @@ void TextEdit::_notification(int p_what) { continue; } - // If we've changed colour we are at the start of a new section, therefore we need to go back to the end + // If we've changed color we are at the start of a new section, therefore we need to go back to the end // of the previous section to draw it, we'll also add the character back on. if (color != previous_color) { characters--; @@ -1219,7 +1219,7 @@ void TextEdit::_notification(int p_what) { int sel_from = (line > get_selection_from_line(c)) ? TS->shaped_text_get_range(rid).x : get_selection_from_column(c); int sel_to = (line < get_selection_to_line(c)) ? TS->shaped_text_get_range(rid).y : get_selection_to_column(c); - if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && override_selected_font_color) { + if (glyphs[j].start >= sel_from && glyphs[j].end <= sel_to && use_selected_font_color) { gl_color = font_selected_color; } } @@ -2051,7 +2051,8 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { } if (is_shortcut_keys_enabled()) { - // SELECT ALL, SELECT WORD UNDER CARET, CUT, COPY, PASTE. + // SELECT ALL, SELECT WORD UNDER CARET, ADD SELECTION FOR NEXT OCCURRENCE, + // CLEAR CARETS AND SELECTIONS, CUT, COPY, PASTE. if (k->is_action("ui_text_select_all", true)) { select_all(); accept_event(); @@ -2062,6 +2063,18 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } + if (k->is_action("ui_text_add_selection_for_next_occurrence", true)) { + add_selection_for_next_occurrence(); + accept_event(); + return; + } + if (k->is_action("ui_text_clear_carets_and_selection", true)) { + // Since the default shortcut is ESC, accepts the event only if it's actually performed. + if (_clear_carets_and_selection()) { + accept_event(); + return; + } + } if (k->is_action("ui_cut", true)) { cut(); accept_event(); @@ -2089,6 +2102,17 @@ void TextEdit::gui_input(const Ref<InputEvent> &p_gui_input) { accept_event(); return; } + + if (k->is_action("ui_text_caret_add_below", true)) { + add_caret_at_carets(true); + accept_event(); + return; + } + if (k->is_action("ui_text_caret_add_above", true)) { + add_caret_at_carets(false); + accept_event(); + return; + } } // MISC. @@ -2635,7 +2659,7 @@ void TextEdit::_do_backspace(bool p_word, bool p_all_to_left) { set_caret_line(get_caret_line(caret_idx), false, true, 0, caret_idx); set_caret_column(column, caret_idx == 0, caret_idx); - // Now we can clean up the overlaping caret. + // Now we can clean up the overlapping caret. if (overlapping_caret_index != -1) { backspace(overlapping_caret_index); i++; @@ -2803,6 +2827,65 @@ void TextEdit::_move_caret_document_end(bool p_select) { } } +bool TextEdit::_clear_carets_and_selection() { + if (get_caret_count() > 1) { + remove_secondary_carets(); + return true; + } + + if (has_selection()) { + deselect(); + return true; + } + + return false; +} + +void TextEdit::_get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x) const { + if (p_last_fit_x == -1) { + p_last_fit_x = _get_column_x_offset_for_line(p_old_column, p_old_line, p_old_column); + } + + // Calculate the new line and wrap index + p_new_line = p_old_line; + int caret_wrap_index = p_old_wrap_index; + if (p_below) { + if (caret_wrap_index < get_line_wrap_count(p_new_line)) { + caret_wrap_index++; + } else { + p_new_line++; + caret_wrap_index = 0; + } + } else { + if (caret_wrap_index == 0) { + p_new_line--; + caret_wrap_index = get_line_wrap_count(p_new_line); + } else { + caret_wrap_index--; + } + } + + // Boundary checks + if (p_new_line < 0) { + p_new_line = 0; + } + if (p_new_line >= text.size()) { + p_new_line = text.size() - 1; + } + + p_new_column = _get_char_pos_for_line(p_last_fit_x, p_new_line, caret_wrap_index); + if (p_new_column != 0 && get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && caret_wrap_index < get_line_wrap_count(p_new_line)) { + Vector<String> rows = get_line_wrapped_text(p_new_line); + int row_end_col = 0; + for (int i = 0; i < caret_wrap_index + 1; i++) { + row_end_col += rows[i].length(); + } + if (p_new_column >= row_end_col) { + p_new_column -= 1; + } + } +} + void TextEdit::_update_placeholder() { if (font.is_null() || font_size <= 0) { return; // Not in tree? @@ -2861,6 +2944,7 @@ void TextEdit::_update_caches() { /* Selection */ font_selected_color = get_theme_color(SNAME("font_selected_color")); selection_color = get_theme_color(SNAME("selection_color")); + use_selected_font_color = font_selected_color != Color(0, 0, 0, 0); /* Visual. */ style_normal = get_theme_stylebox(SNAME("normal")); @@ -3810,6 +3894,9 @@ void TextEdit::undo() { return; } + if (in_action) { + pending_action_end = true; + } _push_current_op(); if (undo_stack_pos == nullptr) { @@ -3871,6 +3958,9 @@ void TextEdit::redo() { return; } + if (in_action) { + pending_action_end = true; + } _push_current_op(); if (undo_stack_pos == nullptr) { @@ -4504,6 +4594,68 @@ int TextEdit::get_caret_count() const { return carets.size(); } +void TextEdit::add_caret_at_carets(bool p_below) { + Vector<int> caret_edit_order = get_caret_index_edit_order(); + for (const int &caret_index : caret_edit_order) { + const int caret_line = get_caret_line(caret_index); + const int caret_column = get_caret_column(caret_index); + + // The last fit x will be cleared if the caret has a selection, + // but if it does not have a selection the last fit x will be + // transferred to the new caret + int caret_from_column = 0, caret_to_column = 0, caret_last_fit_x = carets[caret_index].last_fit_x; + if (has_selection(caret_index)) { + // If the selection goes over multiple lines, deselect it. + if (get_selection_from_line(caret_index) != get_selection_to_line(caret_index)) { + deselect(caret_index); + } else { + caret_from_column = get_selection_from_column(caret_index); + caret_to_column = get_selection_to_column(caret_index); + caret_last_fit_x = -1; + carets.write[caret_index].last_fit_x = _get_column_x_offset_for_line(caret_column, caret_line, caret_column); + } + } + + // Get the line and column of the new caret as if you would move the caret by pressing the arrow keys + int new_caret_line, new_caret_column, new_caret_from_column = 0, new_caret_to_column = 0; + _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_column, p_below, new_caret_line, new_caret_column, caret_last_fit_x); + + // If the caret does have a selection calculate the new from and to columns + if (caret_from_column != caret_to_column) { + // We only need to calculate the selection columns if the column of the caret changed + if (caret_column != new_caret_column) { + int _; // unused placeholder for p_new_line + _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_from_column, p_below, _, new_caret_from_column); + _get_above_below_caret_line_column(caret_line, get_caret_wrap_index(caret_index), caret_to_column, p_below, _, new_caret_to_column); + } else { + new_caret_from_column = caret_from_column; + new_caret_to_column = caret_to_column; + } + } + + // Add the new caret + const int new_caret_index = add_caret(new_caret_line, new_caret_column); + + if (new_caret_index == -1) { + continue; + } + // Also add the selection if there should be one + if (new_caret_from_column != new_caret_to_column) { + select(new_caret_line, new_caret_from_column, new_caret_line, new_caret_to_column, new_caret_index); + // Necessary to properly modify the selection after adding the new caret + carets.write[new_caret_index].selection.selecting_line = new_caret_line; + carets.write[new_caret_index].selection.selecting_column = new_caret_column == new_caret_from_column ? new_caret_to_column : new_caret_from_column; + continue; + } + + // Copy the last fit x over + carets.write[new_caret_index].last_fit_x = carets[caret_index].last_fit_x; + } + + merge_overlapping_carets(); + queue_redraw(); +} + Vector<int> TextEdit::get_caret_index_edit_order() { if (!caret_index_edit_dirty) { return caret_index_edit_order; @@ -4739,14 +4891,6 @@ bool TextEdit::is_drag_and_drop_selection_enabled() const { return drag_and_drop_selection_enabled; } -void TextEdit::set_override_selected_font_color(bool p_override_selected_font_color) { - override_selected_font_color = p_override_selected_font_color; -} - -bool TextEdit::is_overriding_selected_font_color() const { - return override_selected_font_color; -} - void TextEdit::set_selection_mode(SelectionMode p_mode, int p_line, int p_column, int p_caret) { ERR_FAIL_INDEX(p_caret, carets.size()); @@ -4832,6 +4976,45 @@ void TextEdit::select_word_under_caret(int p_caret) { merge_overlapping_carets(); } +void TextEdit::add_selection_for_next_occurrence() { + if (!selecting_enabled || !is_multiple_carets_enabled()) { + return; + } + + if (text.size() == 1 && text[0].length() == 0) { + return; + } + + // Always use the last caret, to correctly search for + // the next occurrence that comes after this caret. + int caret = get_caret_count() - 1; + + if (!has_selection(caret)) { + select_word_under_caret(caret); + return; + } + + const String &highlighted_text = get_selected_text(caret); + int column = get_selection_from_column(caret) + 1; + int line = get_caret_line(caret); + + const Point2i next_occurrence = search(highlighted_text, SEARCH_MATCH_CASE, line, column); + + if (next_occurrence.x == -1 || next_occurrence.y == -1) { + return; + } + + int to_column = get_selection_to_column(caret) + 1; + int end = next_occurrence.x + (to_column - column); + int new_caret = add_caret(next_occurrence.y, end); + + if (new_caret != -1) { + select(next_occurrence.y, next_occurrence.x, next_occurrence.y, end, new_caret); + adjust_viewport_to_caret(new_caret); + merge_overlapping_carets(); + } +} + void TextEdit::select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret) { ERR_FAIL_INDEX(p_caret, carets.size()); if (!selecting_enabled) { @@ -5959,6 +6142,7 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_secondary_carets"), &TextEdit::remove_secondary_carets); ClassDB::bind_method(D_METHOD("merge_overlapping_carets"), &TextEdit::merge_overlapping_carets); ClassDB::bind_method(D_METHOD("get_caret_count"), &TextEdit::get_caret_count); + ClassDB::bind_method(D_METHOD("add_caret_at_carets", "below"), &TextEdit::add_caret_at_carets); ClassDB::bind_method(D_METHOD("get_caret_index_edit_order"), &TextEdit::get_caret_index_edit_order); ClassDB::bind_method(D_METHOD("adjust_carets_after_edit", "caret", "from_line", "from_col", "to_line", "to_col"), &TextEdit::adjust_carets_after_edit); @@ -5992,14 +6176,12 @@ void TextEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_and_drop_selection_enabled", "enable"), &TextEdit::set_drag_and_drop_selection_enabled); ClassDB::bind_method(D_METHOD("is_drag_and_drop_selection_enabled"), &TextEdit::is_drag_and_drop_selection_enabled); - ClassDB::bind_method(D_METHOD("set_override_selected_font_color", "override"), &TextEdit::set_override_selected_font_color); - ClassDB::bind_method(D_METHOD("is_overriding_selected_font_color"), &TextEdit::is_overriding_selected_font_color); - ClassDB::bind_method(D_METHOD("set_selection_mode", "mode", "line", "column", "caret_index"), &TextEdit::set_selection_mode, DEFVAL(-1), DEFVAL(-1), DEFVAL(0)); ClassDB::bind_method(D_METHOD("get_selection_mode"), &TextEdit::get_selection_mode); ClassDB::bind_method(D_METHOD("select_all"), &TextEdit::select_all); ClassDB::bind_method(D_METHOD("select_word_under_caret", "caret_index"), &TextEdit::select_word_under_caret, DEFVAL(-1)); + ClassDB::bind_method(D_METHOD("add_selection_for_next_occurrence"), &TextEdit::add_selection_for_next_occurrence); ClassDB::bind_method(D_METHOD("select", "from_line", "from_column", "to_line", "to_column", "caret_index"), &TextEdit::select, DEFVAL(0)); ClassDB::bind_method(D_METHOD("has_selection", "caret_index"), &TextEdit::has_selection, DEFVAL(-1)); @@ -6163,7 +6345,6 @@ void TextEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "middle_mouse_paste_enabled"), "set_middle_mouse_paste_enabled", "is_middle_mouse_paste_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "wrap_mode", PROPERTY_HINT_ENUM, "None,Boundary"), "set_line_wrapping_mode", "get_line_wrapping_mode"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_selected_font_color"), "set_override_selected_font_color", "is_overriding_selected_font_color"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_all_occurrences"), "set_highlight_all_occurrences", "is_highlight_all_occurrences_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "highlight_current_line"), "set_highlight_current_line", "is_highlight_current_line_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); @@ -6379,7 +6560,7 @@ void TextEdit::_cut_internal(int p_caret) { int indent_level = get_indent_level(cl); double hscroll = get_h_scroll(); - // Check for overlaping carets. + // Check for overlapping carets. // We don't need to worry about selections as that is caught before this entire section. for (int j = i - 1; j >= 0; j--) { if (get_caret_line(caret_edit_order[j]) == cl) { diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index e4af621b73..92acbaf521 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -443,9 +443,9 @@ private: bool deselect_on_focus_loss_enabled = true; bool drag_and_drop_selection_enabled = true; - Color font_selected_color = Color(1, 1, 1); + Color font_selected_color = Color(0, 0, 0, 0); Color selection_color = Color(1, 1, 1); - bool override_selected_font_color = false; + bool use_selected_font_color = false; bool selection_drag_attempt = false; bool dragging_selection = false; @@ -597,6 +597,10 @@ private: void _delete(bool p_word = false, bool p_all_to_right = false); void _move_caret_document_start(bool p_select); void _move_caret_document_end(bool p_select); + bool _clear_carets_and_selection(); + + // Used in add_caret_at_carets + void _get_above_below_caret_line_column(int p_old_line, int p_old_wrap_index, int p_old_column, bool p_below, int &p_new_line, int &p_new_column, int p_last_fit_x = -1) const; protected: void _notification(int p_what); @@ -816,6 +820,7 @@ public: void remove_secondary_carets(); void merge_overlapping_carets(); int get_caret_count() const; + void add_caret_at_carets(bool p_below); Vector<int> get_caret_index_edit_order(); void adjust_carets_after_edit(int p_caret, int p_from_line, int p_from_col, int p_to_line, int p_to_col); @@ -851,6 +856,7 @@ public: void select_all(); void select_word_under_caret(int p_caret = -1); + void add_selection_for_next_occurrence(); void select(int p_from_line, int p_from_column, int p_to_line, int p_to_column, int p_caret = 0); bool has_selection(int p_caret = -1) const; diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index a9982b3ece..48c6dc5cfc 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -510,18 +510,38 @@ void TextureProgressBar::_notification(int p_what) { } pts.append(to); + Ref<AtlasTexture> atlas_progress = progress; + bool valid_atlas_progress = atlas_progress.is_valid() && atlas_progress->get_atlas().is_valid(); + Rect2 region_rect; + Size2 atlas_size; + if (valid_atlas_progress) { + region_rect = atlas_progress->get_region(); + atlas_size = atlas_progress->get_atlas()->get_size(); + } + Vector<Point2> uvs; Vector<Point2> points; - uvs.push_back(get_relative_center()); - points.push_back(progress_offset + s * get_relative_center()); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { continue; } - uvs.push_back(uv); points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y)); + if (valid_atlas_progress) { + uv.x = Math::remap(uv.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x); + uv.y = Math::remap(uv.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y); + } + uvs.push_back(uv); + } + + Point2 center_point = get_relative_center(); + points.push_back(progress_offset + s * center_point); + if (valid_atlas_progress) { + center_point.x = Math::remap(center_point.x, 0, 1, region_rect.position.x / atlas_size.x, (region_rect.position.x + region_rect.size.x) / atlas_size.x); + center_point.y = Math::remap(center_point.y, 0, 1, region_rect.position.y / atlas_size.y, (region_rect.position.y + region_rect.size.y) / atlas_size.y); } + uvs.push_back(center_point); + Vector<Color> colors; colors.push_back(tint_progress); draw_polygon(points, colors, uvs, progress); |