Add feature to Button to make its icon expand/shrink with the button's size

This commit is contained in:
Michael Alexsander Silva Dias 2019-08-20 13:41:14 -03:00
parent 208dd5b4a6
commit 9b09daa8c5
4 changed files with 208 additions and 156 deletions

View File

@ -17,6 +17,9 @@
<member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" default="false"> <member name="clip_text" type="bool" setter="set_clip_text" getter="get_clip_text" default="false">
When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text. When this property is enabled, text that is too large to fit the button is clipped, when disabled the Button will always be wide enough to hold the text.
</member> </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 button's size while keeping its aspect.
</member>
<member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false"> <member name="flat" type="bool" setter="set_flat" getter="is_flat" default="false">
Flat buttons don't display decoration. Flat buttons don't display decoration.
</member> </member>

View File

@ -39,163 +39,192 @@ Size2 Button::get_minimum_size() const {
if (clip_text) if (clip_text)
minsize.width = 0; minsize.width = 0;
Ref<Texture> _icon; if (!expand_icon) {
if (icon.is_null() && has_icon("icon"))
_icon = Control::get_icon("icon");
else
_icon = icon;
if (!_icon.is_null()) {
minsize.height = MAX(minsize.height, _icon->get_height());
minsize.width += _icon->get_width();
if (xl_text != "")
minsize.width += get_constant("hseparation");
}
return get_stylebox("normal")->get_minimum_size() + minsize;
}
void Button::_set_internal_margin(Margin p_margin, float p_value) {
_internal_margin[p_margin] = p_value;
}
void Button::_notification(int p_what) {
if (p_what == NOTIFICATION_TRANSLATION_CHANGED) {
xl_text = tr(text);
minimum_size_changed();
update();
}
if (p_what == NOTIFICATION_DRAW) {
RID ci = get_canvas_item();
Size2 size = get_size();
Color color;
Color color_icon(1, 1, 1, 1);
Ref<StyleBox> style = get_stylebox("normal");
switch (get_draw_mode()) {
case DRAW_NORMAL: {
style = get_stylebox("normal");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color");
if (has_color("icon_color_normal"))
color_icon = get_color("icon_color_normal");
} break;
case DRAW_HOVER_PRESSED: {
if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) {
style = get_stylebox("hover_pressed");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
if (has_color("font_color_hover_pressed"))
color = get_color("font_color_hover_pressed");
else
color = get_color("font_color");
if (has_color("icon_color_hover_pressed"))
color_icon = get_color("icon_color_hover_pressed");
break;
}
FALLTHROUGH;
}
case DRAW_PRESSED: {
style = get_stylebox("pressed");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
if (has_color("font_color_pressed"))
color = get_color("font_color_pressed");
else
color = get_color("font_color");
if (has_color("icon_color_pressed"))
color_icon = get_color("icon_color_pressed");
} break;
case DRAW_HOVER: {
style = get_stylebox("hover");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color_hover");
if (has_color("icon_color_hover"))
color_icon = get_color("icon_color_hover");
} break;
case DRAW_DISABLED: {
style = get_stylebox("disabled");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color_disabled");
if (has_color("icon_color_disabled"))
color_icon = get_color("icon_color_disabled");
} break;
}
if (has_focus()) {
Ref<StyleBox> style2 = get_stylebox("focus");
style2->draw(ci, Rect2(Point2(), size));
}
Ref<Font> font = get_font("font");
Ref<Texture> _icon; Ref<Texture> _icon;
if (icon.is_null() && has_icon("icon")) if (icon.is_null() && has_icon("icon"))
_icon = Control::get_icon("icon"); _icon = Control::get_icon("icon");
else else
_icon = icon; _icon = icon;
Point2 icon_ofs = (!_icon.is_null()) ? Point2(_icon->get_width() + get_constant("hseparation"), 0) : Point2();
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0;
switch (align) {
case ALIGN_LEFT: {
if (_internal_margin[MARGIN_LEFT] > 0) {
text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
} else {
text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x;
}
text_ofs.y += style->get_offset().y;
} break;
case ALIGN_CENTER: {
if (text_ofs.x < 0)
text_ofs.x = 0;
text_ofs += icon_ofs;
text_ofs += style->get_offset();
} break;
case ALIGN_RIGHT: {
if (_internal_margin[MARGIN_RIGHT] > 0) {
text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation");
} else {
text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
}
text_ofs.y += style->get_offset().y;
} break;
}
text_ofs.y += font->get_ascent();
font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1);
if (!_icon.is_null()) { if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y; minsize.height = MAX(minsize.height, _icon->get_height());
if (is_disabled()) minsize.width += _icon->get_width();
color_icon.a = 0.4; if (xl_text != "")
if (_internal_margin[MARGIN_LEFT] > 0) { minsize.width += get_constant("hseparation");
_icon->draw(ci, style->get_offset() + Point2(_internal_margin[MARGIN_LEFT] + get_constant("hseparation"), Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
} else {
_icon->draw(ci, style->get_offset() + Point2(0, Math::floor((valign - _icon->get_height()) / 2.0)), color_icon);
}
} }
} }
return get_stylebox("normal")->get_minimum_size() + minsize;
}
void Button::_set_internal_margin(Margin p_margin, float p_value) {
_internal_margin[p_margin] = p_value;
}
void Button::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_TRANSLATION_CHANGED: {
xl_text = tr(text);
minimum_size_changed();
update();
} break;
case NOTIFICATION_DRAW: {
RID ci = get_canvas_item();
Size2 size = get_size();
Color color;
Color color_icon(1, 1, 1, 1);
Ref<StyleBox> style = get_stylebox("normal");
switch (get_draw_mode()) {
case DRAW_NORMAL: {
style = get_stylebox("normal");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color");
if (has_color("icon_color_normal"))
color_icon = get_color("icon_color_normal");
} break;
case DRAW_HOVER_PRESSED: {
if (has_stylebox("hover_pressed") && has_stylebox_override("hover_pressed")) {
style = get_stylebox("hover_pressed");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
if (has_color("font_color_hover_pressed"))
color = get_color("font_color_hover_pressed");
else
color = get_color("font_color");
if (has_color("icon_color_hover_pressed"))
color_icon = get_color("icon_color_hover_pressed");
break;
}
FALLTHROUGH;
}
case DRAW_PRESSED: {
style = get_stylebox("pressed");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
if (has_color("font_color_pressed"))
color = get_color("font_color_pressed");
else
color = get_color("font_color");
if (has_color("icon_color_pressed"))
color_icon = get_color("icon_color_pressed");
} break;
case DRAW_HOVER: {
style = get_stylebox("hover");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color_hover");
if (has_color("icon_color_hover"))
color_icon = get_color("icon_color_hover");
} break;
case DRAW_DISABLED: {
style = get_stylebox("disabled");
if (!flat)
style->draw(ci, Rect2(Point2(0, 0), size));
color = get_color("font_color_disabled");
if (has_color("icon_color_disabled"))
color_icon = get_color("icon_color_disabled");
} break;
}
if (has_focus()) {
Ref<StyleBox> style2 = get_stylebox("focus");
style2->draw(ci, Rect2(Point2(), size));
}
Ref<Font> font = get_font("font");
Ref<Texture> _icon;
if (icon.is_null() && has_icon("icon"))
_icon = Control::get_icon("icon");
else
_icon = icon;
Rect2 icon_region = Rect2();
if (!_icon.is_null()) {
int valign = size.height - style->get_minimum_size().y;
if (is_disabled()) {
color_icon.a = 0.4;
}
float icon_ofs_region = 0;
if (_internal_margin[MARGIN_LEFT] > 0) {
icon_ofs_region = _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
}
if (expand_icon) {
Size2 _size = get_size() - style->get_offset() * 2;
_size.width -= get_constant("hseparation") + icon_ofs_region;
if (!clip_text)
_size.width -= get_font("font")->get_string_size(xl_text).width;
float icon_width = icon->get_width() * _size.height / icon->get_height();
float icon_height = _size.height;
if (icon_width > _size.width) {
icon_width = _size.width;
icon_height = icon->get_height() * icon_width / icon->get_width();
}
icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, (_size.height - icon_height) / 2), Size2(icon_width, icon_height));
} else {
icon_region = Rect2(style->get_offset() + Point2(icon_ofs_region, Math::floor((valign - _icon->get_height()) / 2.0)), icon->get_size());
}
}
Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + get_constant("hseparation"), 0) : Point2();
int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - font->get_string_size(xl_text) - Point2(_internal_margin[MARGIN_RIGHT] - _internal_margin[MARGIN_LEFT], 0)) / 2.0;
switch (align) {
case ALIGN_LEFT: {
if (_internal_margin[MARGIN_LEFT] > 0) {
text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x + _internal_margin[MARGIN_LEFT] + get_constant("hseparation");
} else {
text_ofs.x = style->get_margin(MARGIN_LEFT) + icon_ofs.x;
}
text_ofs.y += style->get_offset().y;
} break;
case ALIGN_CENTER: {
if (text_ofs.x < 0)
text_ofs.x = 0;
text_ofs += icon_ofs;
text_ofs += style->get_offset();
} break;
case ALIGN_RIGHT: {
if (_internal_margin[MARGIN_RIGHT] > 0) {
text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x - _internal_margin[MARGIN_RIGHT] - get_constant("hseparation");
} else {
text_ofs.x = size.x - style->get_margin(MARGIN_RIGHT) - font->get_string_size(xl_text).x;
}
text_ofs.y += style->get_offset().y;
} break;
}
text_ofs.y += font->get_ascent();
font->draw(ci, text_ofs.floor(), xl_text, color, clip_text ? text_clip : -1);
if (!_icon.is_null() && icon_region.size.width > 0) {
draw_texture_rect_region(_icon, icon_region, Rect2(Point2(), icon->get_size()), color_icon);
}
} break;
}
} }
void Button::set_text(const String &p_text) { void Button::set_text(const String &p_text) {
@ -228,6 +257,18 @@ Ref<Texture> Button::get_icon() const {
return icon; return icon;
} }
void Button::set_expand_icon(bool p_expand_icon) {
expand_icon = p_expand_icon;
update();
minimum_size_changed();
}
bool Button::is_expand_icon() const {
return expand_icon;
}
void Button::set_flat(bool p_flat) { void Button::set_flat(bool p_flat) {
flat = p_flat; flat = p_flat;
@ -269,6 +310,8 @@ void Button::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text); ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text);
ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon); ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon);
ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon); ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon);
ClassDB::bind_method(D_METHOD("set_expand_icon"), &Button::set_expand_icon);
ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat); ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat);
ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text); ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text); ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text);
@ -285,12 +328,14 @@ void Button::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align"); ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_align", "get_text_align");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
} }
Button::Button(const String &p_text) { Button::Button(const String &p_text) {
flat = false; flat = false;
clip_text = false; clip_text = false;
expand_icon = false;
set_mouse_filter(MOUSE_FILTER_STOP); set_mouse_filter(MOUSE_FILTER_STOP);
set_text(p_text); set_text(p_text);
align = ALIGN_CENTER; align = ALIGN_CENTER;

View File

@ -49,6 +49,7 @@ private:
String text; String text;
String xl_text; String xl_text;
Ref<Texture> icon; Ref<Texture> icon;
bool expand_icon;
bool clip_text; bool clip_text;
TextAlign align; TextAlign align;
float _internal_margin[4]; float _internal_margin[4];
@ -59,8 +60,6 @@ protected:
static void _bind_methods(); static void _bind_methods();
public: public:
//
virtual Size2 get_minimum_size() const; virtual Size2 get_minimum_size() const;
void set_text(const String &p_text); void set_text(const String &p_text);
@ -69,6 +68,9 @@ public:
void set_icon(const Ref<Texture> &p_icon); void set_icon(const Ref<Texture> &p_icon);
Ref<Texture> get_icon() const; Ref<Texture> get_icon() const;
void set_expand_icon(bool p_expand_icon);
bool is_expand_icon() const;
void set_flat(bool p_flat); void set_flat(bool p_flat);
bool is_flat() const; bool is_flat() const;

View File

@ -205,24 +205,26 @@ void TextureButton::_notification(int p_what) {
case STRETCH_KEEP_ASPECT_COVERED: { case STRETCH_KEEP_ASPECT_COVERED: {
size = get_size(); size = get_size();
Size2 tex_size = texdraw->get_size(); Size2 tex_size = texdraw->get_size();
Size2 scaleSize(size.width / tex_size.width, size.height / tex_size.height); Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
float scale = scaleSize.width > scaleSize.height ? scaleSize.width : scaleSize.height; float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
Size2 scaledTexSize = tex_size * scale; Size2 scaled_tex_size = tex_size * scale;
Point2 ofs2 = ((scaledTexSize - size) / scale).abs() / 2.0f; Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
_texture_region = Rect2(ofs2, size / scale); _texture_region = Rect2(ofs2, size / scale);
} break; } break;
} }
} }
_position_rect = Rect2(ofs, size); _position_rect = Rect2(ofs, size);
if (_tile) if (_tile) {
draw_texture_rect(texdraw, _position_rect, _tile); draw_texture_rect(texdraw, _position_rect, _tile);
else } else {
draw_texture_rect_region(texdraw, _position_rect, _texture_region); draw_texture_rect_region(texdraw, _position_rect, _texture_region);
}
} else { } else {
_position_rect = Rect2(); _position_rect = Rect2();
} }
if (has_focus() && focused.is_valid()) {
if (has_focus() && focused.is_valid()) {
draw_texture_rect(focused, _position_rect, false); draw_texture_rect(focused, _position_rect, false);
}; };
} break; } break;