From 0d84640974616eea52f91c734b182902622624ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=B4ng=20V=C4=83n=20T=C3=ACnh?= Date: Tue, 20 Aug 2024 02:03:20 +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 | 6 +++ scene/gui/line_edit.cpp | 103 ++++++++++++++++++++++++++++++++------- scene/gui/line_edit.h | 10 ++++ 3 files changed, 102 insertions(+), 17 deletions(-) diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 3e0c328dcb91..378ac506822a 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -275,6 +275,9 @@ If [code]false[/code], existing text cannot be modified and new text cannot be added. + + When enabled, the button's icon will expand/shrink to fit the LineEdit's size. + 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. @@ -321,6 +324,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]. + + Used with the expand_icon to control the icon size. + If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 99678051346a..e71bfa581ec6 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -350,6 +350,33 @@ 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; + } + + if (p_get_min || !expand_icon) { + real_t height = theme_cache.font->get_height(theme_cache.font_size); + icon_size = Size2(height, height); + } else { + 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; + } + + return icon_size; +} + void LineEdit::unhandled_key_input(const Ref &p_event) { // Return to prevent editing if just focused. if (!editing) { @@ -1162,17 +1189,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. @@ -1547,12 +1578,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)); } } @@ -1598,12 +1630,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)); } } @@ -1941,14 +1974,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. @@ -2041,12 +2075,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; @@ -2496,6 +2532,30 @@ Ref LineEdit::get_right_icon() { return right_icon; } +void LineEdit::set_expand_icon(bool p_enabled) { + if (expand_icon != p_enabled) { + expand_icon = p_enabled; + queue_redraw(); + update_minimum_size(); + } +} + +bool LineEdit::is_expand_icon() const { + return expand_icon; +} + +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; @@ -2588,7 +2648,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)); } @@ -2844,6 +2905,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_icon", "p_enabled"), &LineEdit::set_expand_icon); + ClassDB::bind_method(D_METHOD("is_expand_icon"), &LineEdit::is_expand_icon); + 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); @@ -2910,7 +2975,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"); @@ -2932,6 +2996,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 Behavior", ""); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_right_icon", "get_right_icon"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "right_icon_scale", PROPERTY_HINT_RANGE, "0.3,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..7045be525fd8 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -90,6 +90,7 @@ class LineEdit : public Control { bool editable = false; bool pass = false; bool text_changed_dirty = false; + bool expand_icon = false; bool alt_start = false; bool alt_start_no_hold = false; @@ -145,6 +146,7 @@ class LineEdit : public Control { Ref right_icon; bool flat = false; + float right_icon_scale = 1.0; struct Selection { int begin = 0; @@ -249,6 +251,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 +393,12 @@ class LineEdit : public Control { void set_right_icon(const Ref &p_icon); Ref get_right_icon(); + void set_expand_icon(bool p_enabled); + bool is_expand_icon() 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;