From 270407b20a33ce54c652b1b8d961a340d4a9a6a8 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. Co-authored-by: Tomasz Chabora Co-authored-by: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> --- doc/classes/LineEdit.xml | 15 ++++ scene/gui/line_edit.cpp | 148 +++++++++++++++++++++++++++++++-------- scene/gui/line_edit.h | 16 +++++ 3 files changed, 148 insertions(+), 31 deletions(-) diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index 73d4e50dd70..831660fe1f0 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -310,6 +310,9 @@ If [code]true[/code], the [LineEdit] doesn't display decoration. + + Define the scaling behavior of the [member right_icon]. + If [code]true[/code], the [LineEdit] will not exit edit mode when text is submitted by pressing [code]ui_text_submit[/code] action (by default: [kbd]Enter[/kbd] or [kbd]Kp Enter[/kbd]). @@ -352,6 +355,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 icon_expand_mode] is set to [constant EXPAND_MODE_FIT_TO_LINE_EDIT]. + If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). @@ -539,6 +545,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 69ef0bde1e4..332abef942e 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -370,6 +370,40 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) { } } +Point2 LineEdit::_get_right_icon_size(Ref p_right_icon) const { + Size2 icon_size; + + if (p_right_icon.is_null()) { + return icon_size; + } + + switch (icon_expand_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) { @@ -1255,20 +1289,21 @@ void LineEdit::_notification(int p_what) { 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); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { int total_margin = style->get_margin(SIDE_LEFT) + style->get_margin(SIDE_RIGHT); - int center = int(size.width - total_margin - text_width - r_icon->get_width()) / 2; + int center = int(size.width - total_margin - text_width - right_icon_size.width) / 2; x_ofs = style->get_margin(SIDE_LEFT) + MAX(0, center); } if (rtl) { - x_ofs += r_icon->get_width(); + x_ofs += right_icon_size.width; } } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } 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)); } } } @@ -1409,6 +1444,7 @@ void LineEdit::_notification(int p_what) { 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); Color color_icon(1, 1, 1, !is_editable() ? .5 * .9 : .9); if (display_clear_icon) { if (clear_button_status.press_attempt && clear_button_status.pressing_inside) { @@ -1418,31 +1454,32 @@ void LineEdit::_notification(int p_what) { } } - Point2 icon_pos = Point2(width - r_icon->get_width() - style->get_margin(SIDE_RIGHT), height / 2 - r_icon->get_height() / 2); + Point2 icon_pos = Point2(width - right_icon_size.width - style->get_margin(SIDE_RIGHT), height / 2 - right_icon_size.height / 2); if (rtl) { icon_pos.x = style->get_margin(SIDE_LEFT); } - r_icon->draw(ci, icon_pos, color_icon); + Rect2 icon_region = Rect2(icon_pos, 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)) { int total_margin = style->get_margin(SIDE_LEFT) + style->get_margin(SIDE_RIGHT); - int center = int(size.width - total_margin - text_width - r_icon->get_width()) / 2; + int center = int(size.width - total_margin - text_width - right_icon_size.width) / 2; x_ofs = style->get_margin(SIDE_LEFT) + MAX(0, center); } if (rtl) { - x_ofs += r_icon->get_width(); + x_ofs += right_icon_size.width; } } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } 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)); } } if (!rtl) { - ofs_max -= r_icon->get_width(); + ofs_max -= right_icon_size.width; } } @@ -1842,20 +1879,21 @@ 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); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { int total_margin = style->get_margin(SIDE_LEFT) + style->get_margin(SIDE_RIGHT); - int center = int(get_size().width - total_margin - text_width - r_icon->get_width()) / 2; + int center = int(get_size().width - total_margin - text_width - right_icon_size.width) / 2; x_ofs = style->get_margin(SIDE_LEFT) + MAX(0, center); } if (rtl) { - x_ofs += r_icon->get_width(); + x_ofs += right_icon_size.width; } } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } 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)); } } } @@ -1907,20 +1945,21 @@ 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); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { int total_margin = style->get_margin(SIDE_LEFT) + style->get_margin(SIDE_RIGHT); - int center = int(get_size().width - total_margin - text_width - r_icon->get_width()) / 2; + int center = int(get_size().width - total_margin - text_width - right_icon_size.width) / 2; x_ofs = style->get_margin(SIDE_LEFT) + MAX(0, center); } if (rtl) { - x_ofs += r_icon->get_width(); + x_ofs += right_icon_size.width; } } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } 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)); } } } @@ -2270,28 +2309,29 @@ 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); if (alignment == HORIZONTAL_ALIGNMENT_CENTER) { if (Math::is_zero_approx(scroll_offset)) { int total_margin = style->get_margin(SIDE_LEFT) + style->get_margin(SIDE_RIGHT); - int center = int(get_size().width - total_margin - text_width - r_icon->get_width()) / 2; + int center = int(get_size().width - total_margin - text_width - right_icon_size.width) / 2; x_ofs = style->get_margin(SIDE_LEFT) + MAX(0, center); } if (rtl) { - x_ofs += r_icon->get_width(); + x_ofs += right_icon_size.width; } } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } else { if (rtl) { - x_ofs = MAX(style->get_margin(SIDE_LEFT) + r_icon->get_width(), x_ofs); + x_ofs = MAX(style->get_margin(SIDE_LEFT) + right_icon_size.width, x_ofs); } 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)); } } } if (!rtl) { - ofs_max -= r_icon->get_width(); + ofs_max -= right_icon_size.width; } } @@ -2404,12 +2444,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); + 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); + 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; @@ -2942,6 +2984,35 @@ Ref LineEdit::get_right_icon() { return right_icon; } +void LineEdit::set_icon_expand_mode(ExpandMode p_mode) { + if (icon_expand_mode == p_mode) { + return; + } + + icon_expand_mode = p_mode; + queue_redraw(); + update_minimum_size(); + notify_property_list_changed(); +} + +LineEdit::ExpandMode LineEdit::get_icon_expand_mode() const { + return icon_expand_mode; +} + +void LineEdit::set_right_icon_scale(float p_scale) { + if (right_icon_scale == p_scale) { + return; + } + + right_icon_scale = p_scale; + 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; @@ -3042,7 +3113,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); + t_width -= right_icon_size.width; } TS->shaped_text_fit_to_width(text_rid, MAX(t_width, full_width)); } @@ -3221,6 +3293,8 @@ 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 (icon_expand_mode != EXPAND_MODE_FIT_TO_LINE_EDIT && p_property.name == "right_icon_scale") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; } } @@ -3319,6 +3393,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_icon_expand_mode", "mode"), &LineEdit::set_icon_expand_mode); + ClassDB::bind_method(D_METHOD("get_icon_expand_mode"), &LineEdit::get_icon_expand_mode); + ClassDB::bind_method(D_METHOD("set_right_icon_scale", "scale"), &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); @@ -3371,6 +3449,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"); @@ -3387,7 +3469,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"); @@ -3414,6 +3495,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, "icon_expand_mode", PROPERTY_HINT_ENUM, "Original,Fit to Text,Fit to LineEdit"), "set_icon_expand_mode", "get_icon_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 c8ca6c7e3bb..b02fc7dea79 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -83,6 +83,12 @@ public: 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; @@ -91,6 +97,7 @@ private: bool editable = false; bool pass = false; bool text_changed_dirty = false; + ExpandMode icon_expand_mode = EXPAND_MODE_ORIGINAL_SIZE; enum AltInputMode { ALT_INPUT_NONE, @@ -158,6 +165,7 @@ private: Ref right_icon; bool flat = false; + float right_icon_scale = 1.0; struct Selection { int begin = 0; @@ -264,6 +272,7 @@ private: void _texture_changed(); void _edit(bool p_show_virtual_keyboard = true, bool p_hide_focus = false); + Point2 _get_right_icon_size(Ref p_right_icon) const; protected: bool _is_over_clear_button(const Point2 &p_pos) const; @@ -430,6 +439,12 @@ public: void set_right_icon(const Ref &p_icon); Ref get_right_icon(); + void set_icon_expand_mode(ExpandMode p_mode); + ExpandMode get_icon_expand_mode() const; + + void set_right_icon_scale(float p_scale); + float get_right_icon_scale() const; + void set_flat(bool p_enabled); bool is_flat() const; @@ -449,3 +464,4 @@ public: VARIANT_ENUM_CAST(LineEdit::MenuItems); VARIANT_ENUM_CAST(LineEdit::VirtualKeyboardType); +VARIANT_ENUM_CAST(LineEdit::ExpandMode);