Skip to content

Commit

Permalink
Implement dynamic scaling of the LineEdit right icon based on control…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
nongvantinh committed Oct 9, 2024
1 parent 4c4e673 commit 965d970
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 17 deletions.
6 changes: 6 additions & 0 deletions doc/classes/LineEdit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@
<member name="editable" type="bool" setter="set_editable" getter="is_editable" default="true" keywords="readonly, disabled, enabled">
If [code]false[/code], existing text cannot be modified and new text cannot be added.
</member>
<member name="expand_icon" type="bool" setter="set_expand_icon" getter="is_expand_icon" default="false">
When enabled, the button's icon will expand/shrink to fit the LineEdit's size.
</member>
<member name="expand_to_text_length" type="bool" setter="set_expand_to_text_length_enabled" getter="is_expand_to_text_length_enabled" default="false">
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.
</member>
Expand Down Expand Up @@ -321,6 +324,9 @@
<member name="right_icon" type="Texture2D" setter="set_right_icon" getter="get_right_icon">
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].
</member>
<member name="right_icon_scale" type="float" setter="set_right_icon_scale" getter="get_right_icon_scale" default="1.0">
Used with the expand_icon to control the icon size.
</member>
<member name="secret" type="bool" setter="set_secret" getter="is_secret" default="false">
If [code]true[/code], every character is replaced with the secret character (see [member secret_character]).
</member>
Expand Down
103 changes: 86 additions & 17 deletions scene/gui/line_edit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,33 @@ void LineEdit::_delete(bool p_word, bool p_all_to_right) {
}
}

Point2 LineEdit::_get_right_icon_size(Ref<Texture2D> 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<InputEvent> &p_event) {
// Return to prevent editing if just focused.
if (!editing) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<Texture2D> 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));
}
}

Expand Down Expand Up @@ -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<Texture2D> 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));
}
}

Expand Down Expand Up @@ -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<Texture2D> 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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -2496,6 +2532,30 @@ Ref<Texture2D> 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;
Expand Down Expand Up @@ -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<Texture2D> 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));
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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");
Expand All @@ -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);
Expand Down
10 changes: 10 additions & 0 deletions scene/gui/line_edit.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -145,6 +146,7 @@ class LineEdit : public Control {

Ref<Texture2D> right_icon;
bool flat = false;
float right_icon_scale = 1.0;

struct Selection {
int begin = 0;
Expand Down Expand Up @@ -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<Texture2D> p_right_icon, bool p_get_min) const;

protected:
bool _is_over_clear_button(const Point2 &p_pos) const;

Expand Down Expand Up @@ -389,6 +393,12 @@ class LineEdit : public Control {
void set_right_icon(const Ref<Texture2D> &p_icon);
Ref<Texture2D> 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;

Expand Down

0 comments on commit 965d970

Please sign in to comment.