From 533d0b9d99be6b57f9dba5fd8db274c8694d7268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=B4ng=20V=C4=83n=20T=C3=ACnh?= Date: Fri, 22 Nov 2024 21:27:36 +0700 Subject: [PATCH] Implement dynamic scaling of the LineEdit right icon based on control size and scale factor The implementation allows the LineEdit node to scale the right icon to match the font size first. Then, when the `expand_icon` option is enabled, the icon will expand to the full height of the node. The scale of the icon can then be controlled using the scale factor. --- doc/classes/LineEdit.xml | 15 +++++ scene/gui/line_edit.cpp | 118 +++++++++++++++++++++++++++++++++------ scene/gui/line_edit.h | 17 ++++++ 3 files changed, 133 insertions(+), 17 deletions(-) diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 91c9072f73e9..66956733c7e3 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -276,6 +276,9 @@ If [code]false[/code], existing text cannot be modified and new text cannot be added. + + Define the scaling behavior of the right icon + If [code]true[/code], the [LineEdit] width will increase to stay longer than the [member text]. It will [b]not[/b] compress if the [member text] is shortened. @@ -322,6 +325,9 @@ Sets the icon that will appear in the right end of the [LineEdit] if there's no [member text], or always, if [member clear_button_enabled] is set to [code]false[/code]. + + Scale ratio of the icon when [member expand_mode] is set to fit to LineEdit. + If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). @@ -503,6 +509,15 @@ Virtual keyboard with additional keys to assist with typing URLs. + + Use the original size for the right icon. + + + Scale the right icon's size to match the size of the text. + + + Scale the right icon to fit the LineEdit. + diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index b7c73261720b..b6c8940dfa54 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -352,6 +352,41 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } +Point2 LineEdit::_get_right_icon_size(Ref p_right_icon, bool p_get_min) const { + Size2 icon_size; + + if (!p_right_icon.is_valid()) { + return icon_size; + } + + ExpandMode mode = p_get_min ? EXPAND_MODE_FIT_TO_TEXT : expand_mode; + switch (mode) { + default: + case LineEdit::EXPAND_MODE_ORIGINAL_SIZE: + icon_size = p_right_icon->get_size(); + break; + case LineEdit::EXPAND_MODE_FIT_TO_TEXT: { + real_t height = theme_cache.font->get_height(theme_cache.font_size); + icon_size = Size2(height, height); + } break; + case LineEdit::EXPAND_MODE_FIT_TO_LINE_EDIT: { + icon_size = p_right_icon->get_size(); + Point2 size = get_size(); + float icon_width = icon_size.width * size.height / icon_size.height; + float icon_height = size.height; + + if (icon_width > size.width) { + icon_width = size.width; + icon_height = icon_size.height * icon_width / icon_size.width; + } + + icon_size = Size2(icon_width, icon_height) * right_icon_scale; + } break; + } + + return icon_size; +} + void LineEdit::unhandled_key_input(const Ref &p_event) { // Return to prevent editing if just focused. if (!editing) { @@ -1164,17 +1199,21 @@ void LineEdit::_notification(int p_what) { } } - r_icon->draw(ci, Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2), color_icon); + Point2 right_icon_size = _get_right_icon_size(r_icon, false); + Rect2 icon_region = Rect2(Point2(width - right_icon_size.width - style->get_margin(SIDE_RIGHT), height / 2 - right_icon_size.height / 2), + right_icon_size); + + draw_texture_rect(r_icon, icon_region, false, color_icon); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(size.width - text_width - right_icon_size.width - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - right_icon_size.width - style->get_margin(SIDE_RIGHT)); } - ofs_max -= r_icon->get_width(); + ofs_max -= right_icon_size.width; } // Draw selections rects. @@ -1545,12 +1584,13 @@ void LineEdit::set_caret_at_pixel_pos(int p_x) { bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { Ref r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; + Point2 right_icon_size = _get_right_icon_size(r_icon, false); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - right_icon_size.width - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - right_icon_size.width - style->get_margin(SIDE_RIGHT)); } } @@ -1596,12 +1636,13 @@ Vector2 LineEdit::get_caret_pixel_pos() { bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { Ref r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; + Point2 right_icon_size = _get_right_icon_size(r_icon, false); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - right_icon_size.width - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - right_icon_size.width - style->get_margin(SIDE_RIGHT)); } } @@ -1939,14 +1980,15 @@ void LineEdit::set_caret_column(int p_column) { bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { Ref r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; + Point2 right_icon_size = _get_right_icon_size(r_icon, false); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { - x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - r_icon->get_width() - style->get_margin(SIDE_RIGHT) * 2) / 2); + x_ofs = MAX(style->get_margin(SIDE_LEFT), int(get_size().width - text_width - right_icon_size.width - style->get_margin(SIDE_RIGHT) * 2) / 2); } } else { - x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - r_icon->get_width() - style->get_margin(SIDE_RIGHT)); + x_ofs = MAX(style->get_margin(SIDE_LEFT), x_ofs - right_icon_size.width - style->get_margin(SIDE_RIGHT)); } - ofs_max -= r_icon->get_width(); + ofs_max -= right_icon_size.width; } // Note: Use two coordinates to fit IME input range. @@ -2039,12 +2081,14 @@ Size2 LineEdit::get_minimum_size() const { // Take icons into account. int icon_max_width = 0; if (right_icon.is_valid()) { - min_size.height = MAX(min_size.height, right_icon->get_height()); - icon_max_width = right_icon->get_width(); + Point2 right_icon_size = _get_right_icon_size(right_icon, true); + min_size.height = MAX(min_size.height, right_icon_size.height); + icon_max_width = right_icon_size.width; } if (clear_button_enabled) { - min_size.height = MAX(min_size.height, theme_cache.clear_icon->get_height()); - icon_max_width = MAX(icon_max_width, theme_cache.clear_icon->get_width()); + Point2 right_icon_size = _get_right_icon_size(theme_cache.clear_icon, true); + min_size.height = MAX(min_size.height, right_icon_size.height); + icon_max_width = MAX(icon_max_width, right_icon_size.width); } min_size.width += icon_max_width; @@ -2494,6 +2538,31 @@ Ref LineEdit::get_right_icon() { return right_icon; } +void LineEdit::set_expand_mode(ExpandMode p_mode) { + if (expand_mode != p_mode) { + expand_mode = p_mode; + queue_redraw(); + update_minimum_size(); + notify_property_list_changed(); + } +} + +LineEdit::ExpandMode LineEdit::get_expand_mode() const { + return expand_mode; +} + +void LineEdit::set_right_icon_scale(float p_ratio) { + if (right_icon_scale != p_ratio) { + right_icon_scale = p_ratio; + queue_redraw(); + update_minimum_size(); + } +} + +float LineEdit::get_right_icon_scale() const { + return right_icon_scale; +} + void LineEdit::set_flat(bool p_enabled) { if (flat != p_enabled) { flat = p_enabled; @@ -2586,7 +2655,8 @@ void LineEdit::_fit_to_width() { bool display_clear_icon = !using_placeholder && is_editable() && clear_button_enabled; if (right_icon.is_valid() || display_clear_icon) { Ref r_icon = display_clear_icon ? theme_cache.clear_icon : right_icon; - t_width -= r_icon->get_width(); + Point2 right_icon_size = _get_right_icon_size(r_icon, false); + t_width -= right_icon_size.width; } TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); } @@ -2754,6 +2824,8 @@ void LineEdit::_update_context_menu() { 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; + } else if (expand_mode != EXPAND_MODE_FIT_TO_LINE_EDIT && p_property.name == "right_icon_scale") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -2842,6 +2914,10 @@ void LineEdit::_bind_methods() { ClassDB::bind_method(D_METHOD("is_drag_and_drop_selection_enabled"), &LineEdit::is_drag_and_drop_selection_enabled); ClassDB::bind_method(D_METHOD("set_right_icon", "icon"), &LineEdit::set_right_icon); ClassDB::bind_method(D_METHOD("get_right_icon"), &LineEdit::get_right_icon); + ClassDB::bind_method(D_METHOD("set_expand_mode", "mode"), &LineEdit::set_expand_mode); + ClassDB::bind_method(D_METHOD("get_expand_mode"), &LineEdit::get_expand_mode); + ClassDB::bind_method(D_METHOD("set_right_icon_scale", "p_ratio"), &LineEdit::set_right_icon_scale); + ClassDB::bind_method(D_METHOD("get_right_icon_scale"), &LineEdit::get_right_icon_scale); 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); @@ -2893,6 +2969,10 @@ void LineEdit::_bind_methods() { BIND_ENUM_CONSTANT(KEYBOARD_TYPE_PASSWORD); BIND_ENUM_CONSTANT(KEYBOARD_TYPE_URL); + BIND_ENUM_CONSTANT(EXPAND_MODE_ORIGINAL_SIZE); + BIND_ENUM_CONSTANT(EXPAND_MODE_FIT_TO_TEXT); + BIND_ENUM_CONSTANT(EXPAND_MODE_FIT_TO_LINE_EDIT); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "placeholder_text"), "set_placeholder", "get_placeholder"); ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment"); @@ -2908,7 +2988,6 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deselect_on_focus_loss_enabled"), "set_deselect_on_focus_loss_enabled", "is_deselect_on_focus_loss_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "drag_and_drop_selection_enabled"), "set_drag_and_drop_selection_enabled", "is_drag_and_drop_selection_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_right_icon", "get_right_icon"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus"); @@ -2930,6 +3009,11 @@ void LineEdit::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "structured_text_bidi_override", PROPERTY_HINT_ENUM, "Default,URI,File,Email,List,None,Custom"), "set_structured_text_bidi_override", "get_structured_text_bidi_override"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "structured_text_bidi_override_options"), "set_structured_text_bidi_override_options", "get_structured_text_bidi_override_options"); + ADD_GROUP("Icon", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_right_icon", "get_right_icon"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "expand_mode", PROPERTY_HINT_ENUM, "Original, Fit to Text, Fit to LineEdit"), "set_expand_mode", "get_expand_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "right_icon_scale", PROPERTY_HINT_RANGE, "0.1,1.0,0.01"), "set_right_icon_scale", "get_right_icon_scale"); + BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, normal); BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, read_only); BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, LineEdit, focus); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 9253dd871197..6e92dab84b39 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -83,6 +83,12 @@ class LineEdit : public Control { KEYBOARD_TYPE_URL }; + enum ExpandMode { + EXPAND_MODE_ORIGINAL_SIZE, + EXPAND_MODE_FIT_TO_TEXT, + EXPAND_MODE_FIT_TO_LINE_EDIT + }; + private: HorizontalAlignment alignment = HORIZONTAL_ALIGNMENT_LEFT; @@ -90,6 +96,7 @@ class LineEdit : public Control { bool editable = false; bool pass = false; bool text_changed_dirty = false; + ExpandMode expand_mode = EXPAND_MODE_ORIGINAL_SIZE; bool alt_start = false; bool alt_start_no_hold = false; @@ -145,6 +152,7 @@ class LineEdit : public Control { Ref right_icon; bool flat = false; + float right_icon_scale = 1.0; struct Selection { int begin = 0; @@ -249,6 +257,8 @@ class LineEdit : public Control { void _backspace(bool p_word = false, bool p_all_to_left = false); void _delete(bool p_word = false, bool p_all_to_right = false); + Point2 _get_right_icon_size(Ref p_right_icon, bool p_get_min) const; + protected: bool _is_over_clear_button(const Point2 &p_pos) const; @@ -389,6 +399,12 @@ class LineEdit : public Control { void set_right_icon(const Ref &p_icon); Ref get_right_icon(); + void set_expand_mode(ExpandMode p_mode); + ExpandMode get_expand_mode() const; + + void set_right_icon_scale(float p_ratio); + float get_right_icon_scale() const; + void set_flat(bool p_enabled); bool is_flat() const; @@ -408,5 +424,6 @@ class LineEdit : public Control { VARIANT_ENUM_CAST(LineEdit::MenuItems); VARIANT_ENUM_CAST(LineEdit::VirtualKeyboardType); +VARIANT_ENUM_CAST(LineEdit::ExpandMode); #endif // LINE_EDIT_H