1/**************************************************************************/
2/* button.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "button.h"
32
33#include "core/string/translation.h"
34#include "scene/theme/theme_db.h"
35#include "servers/rendering_server.h"
36
37Size2 Button::get_minimum_size() const {
38 Ref<Texture2D> _icon = icon;
39 if (_icon.is_null() && has_theme_icon(SNAME("icon"))) {
40 _icon = theme_cache.icon;
41 }
42
43 return get_minimum_size_for_text_and_icon("", _icon);
44}
45
46void Button::_set_internal_margin(Side p_side, float p_value) {
47 _internal_margin[p_side] = p_value;
48}
49
50void Button::_queue_update_size_cache() {
51}
52
53void Button::_notification(int p_what) {
54 switch (p_what) {
55 case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
56 queue_redraw();
57 } break;
58
59 case NOTIFICATION_TRANSLATION_CHANGED: {
60 xl_text = atr(text);
61 _shape();
62
63 update_minimum_size();
64 queue_redraw();
65 } break;
66
67 case NOTIFICATION_THEME_CHANGED: {
68 _shape();
69
70 update_minimum_size();
71 queue_redraw();
72 } break;
73
74 case NOTIFICATION_DRAW: {
75 RID ci = get_canvas_item();
76 Size2 size = get_size();
77 Color color;
78 Color color_icon(1, 1, 1, 1);
79
80 Ref<StyleBox> style = theme_cache.normal;
81 bool rtl = is_layout_rtl();
82 const bool is_clipped = clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING;
83
84 switch (get_draw_mode()) {
85 case DRAW_NORMAL: {
86 if (rtl && has_theme_stylebox(SNAME("normal_mirrored"))) {
87 style = theme_cache.normal_mirrored;
88 } else {
89 style = theme_cache.normal;
90 }
91
92 if (!flat) {
93 style->draw(ci, Rect2(Point2(0, 0), size));
94 }
95
96 // Focus colors only take precedence over normal state.
97 if (has_focus()) {
98 color = theme_cache.font_focus_color;
99 if (has_theme_color(SNAME("icon_focus_color"))) {
100 color_icon = theme_cache.icon_focus_color;
101 }
102 } else {
103 color = theme_cache.font_color;
104 if (has_theme_color(SNAME("icon_normal_color"))) {
105 color_icon = theme_cache.icon_normal_color;
106 }
107 }
108 } break;
109 case DRAW_HOVER_PRESSED: {
110 // Edge case for CheckButton and CheckBox.
111 if (has_theme_stylebox("hover_pressed")) {
112 if (rtl && has_theme_stylebox(SNAME("hover_pressed_mirrored"))) {
113 style = theme_cache.hover_pressed_mirrored;
114 } else {
115 style = theme_cache.hover_pressed;
116 }
117
118 if (!flat) {
119 style->draw(ci, Rect2(Point2(0, 0), size));
120 }
121 if (has_theme_color(SNAME("font_hover_pressed_color"))) {
122 color = theme_cache.font_hover_pressed_color;
123 }
124 if (has_theme_color(SNAME("icon_hover_pressed_color"))) {
125 color_icon = theme_cache.icon_hover_pressed_color;
126 }
127
128 break;
129 }
130 [[fallthrough]];
131 }
132 case DRAW_PRESSED: {
133 if (rtl && has_theme_stylebox(SNAME("pressed_mirrored"))) {
134 style = theme_cache.pressed_mirrored;
135 } else {
136 style = theme_cache.pressed;
137 }
138
139 if (!flat) {
140 style->draw(ci, Rect2(Point2(0, 0), size));
141 }
142 if (has_theme_color(SNAME("font_pressed_color"))) {
143 color = theme_cache.font_pressed_color;
144 } else {
145 color = theme_cache.font_color;
146 }
147 if (has_theme_color(SNAME("icon_pressed_color"))) {
148 color_icon = theme_cache.icon_pressed_color;
149 }
150
151 } break;
152 case DRAW_HOVER: {
153 if (rtl && has_theme_stylebox(SNAME("hover_mirrored"))) {
154 style = theme_cache.hover_mirrored;
155 } else {
156 style = theme_cache.hover;
157 }
158
159 if (!flat) {
160 style->draw(ci, Rect2(Point2(0, 0), size));
161 }
162 color = theme_cache.font_hover_color;
163 if (has_theme_color(SNAME("icon_hover_color"))) {
164 color_icon = theme_cache.icon_hover_color;
165 }
166
167 } break;
168 case DRAW_DISABLED: {
169 if (rtl && has_theme_stylebox(SNAME("disabled_mirrored"))) {
170 style = theme_cache.disabled_mirrored;
171 } else {
172 style = theme_cache.disabled;
173 }
174
175 if (!flat) {
176 style->draw(ci, Rect2(Point2(0, 0), size));
177 }
178 color = theme_cache.font_disabled_color;
179 if (has_theme_color(SNAME("icon_disabled_color"))) {
180 color_icon = theme_cache.icon_disabled_color;
181 } else {
182 color_icon.a = 0.4;
183 }
184
185 } break;
186 }
187
188 if (has_focus()) {
189 Ref<StyleBox> style2 = theme_cache.focus;
190 style2->draw(ci, Rect2(Point2(), size));
191 }
192
193 Ref<Texture2D> _icon;
194 if (icon.is_null() && has_theme_icon(SNAME("icon"))) {
195 _icon = theme_cache.icon;
196 } else {
197 _icon = icon;
198 }
199
200 Rect2 icon_region;
201 HorizontalAlignment icon_align_rtl_checked = horizontal_icon_alignment;
202 HorizontalAlignment align_rtl_checked = alignment;
203 // Swap icon and text alignment sides if right-to-left layout is set.
204 if (rtl) {
205 if (horizontal_icon_alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
206 icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
207 } else if (horizontal_icon_alignment == HORIZONTAL_ALIGNMENT_LEFT) {
208 icon_align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
209 }
210 if (alignment == HORIZONTAL_ALIGNMENT_RIGHT) {
211 align_rtl_checked = HORIZONTAL_ALIGNMENT_LEFT;
212 } else if (alignment == HORIZONTAL_ALIGNMENT_LEFT) {
213 align_rtl_checked = HORIZONTAL_ALIGNMENT_RIGHT;
214 }
215 }
216 if (!_icon.is_null()) {
217 int valign = size.height - style->get_minimum_size().y;
218
219 int voffset = 0;
220 Size2 icon_size = _icon->get_size();
221
222 // Fix vertical size.
223 if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
224 valign -= text_buf->get_size().height;
225 }
226
227 float icon_ofs_region = 0.0;
228 Point2 style_offset;
229 if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
230 style_offset.x = style->get_margin(SIDE_LEFT);
231 if (_internal_margin[SIDE_LEFT] > 0) {
232 icon_ofs_region = _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
233 }
234 } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
235 style_offset.x = 0.0;
236 } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
237 style_offset.x = -style->get_margin(SIDE_RIGHT);
238 if (_internal_margin[SIDE_RIGHT] > 0) {
239 icon_ofs_region = -_internal_margin[SIDE_RIGHT] - theme_cache.h_separation;
240 }
241 }
242 style_offset.y = style->get_margin(SIDE_TOP);
243
244 if (expand_icon) {
245 Size2 _size = get_size() - style->get_offset() * 2;
246 int icon_text_separation = text.is_empty() ? 0 : theme_cache.h_separation;
247 _size.width -= icon_text_separation + icon_ofs_region;
248 if (!is_clipped && icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_CENTER) {
249 _size.width -= text_buf->get_size().width;
250 }
251 if (vertical_icon_alignment != VERTICAL_ALIGNMENT_CENTER) {
252 _size.height -= text_buf->get_size().height;
253 }
254 float icon_width = _icon->get_width() * _size.height / _icon->get_height();
255 float icon_height = _size.height;
256
257 if (icon_width > _size.width) {
258 icon_width = _size.width;
259 icon_height = _icon->get_height() * icon_width / _icon->get_width();
260 }
261
262 icon_size = Size2(icon_width, icon_height);
263 }
264 icon_size = _fit_icon_size(icon_size);
265
266 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) {
267 voffset = -(valign - icon_size.y) / 2;
268 }
269 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_BOTTOM) {
270 voffset = (valign - icon_size.y) / 2 + text_buf->get_size().y;
271 }
272
273 if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
274 icon_region = Rect2(style_offset + Point2(icon_ofs_region, voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
275 } else if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
276 icon_region = Rect2(style_offset + Point2(icon_ofs_region + Math::floor((size.x - icon_size.x) * 0.5), voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
277 } else {
278 icon_region = Rect2(style_offset + Point2(icon_ofs_region + size.x - icon_size.x, voffset + Math::floor((valign - icon_size.y) * 0.5)), icon_size);
279 }
280
281 if (icon_region.size.width > 0) {
282 Rect2 icon_region_rounded = Rect2(icon_region.position.round(), icon_region.size.round());
283 draw_texture_rect(_icon, icon_region_rounded, false, color_icon);
284 }
285 }
286
287 Point2 icon_ofs = !_icon.is_null() ? Point2(icon_region.size.width + theme_cache.h_separation, 0) : Point2();
288 if (align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER && icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_CENTER) {
289 icon_ofs.x = 0.0;
290 }
291
292 int text_clip = size.width - style->get_minimum_size().width - icon_ofs.width;
293 if (_internal_margin[SIDE_LEFT] > 0) {
294 text_clip -= _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
295 }
296 if (_internal_margin[SIDE_RIGHT] > 0) {
297 text_clip -= _internal_margin[SIDE_RIGHT] + theme_cache.h_separation;
298 }
299
300 text_buf->set_width(is_clipped ? text_clip : -1);
301
302 int text_width = MAX(1, is_clipped ? MIN(text_clip, text_buf->get_size().x) : text_buf->get_size().x);
303
304 Point2 text_ofs = (size - style->get_minimum_size() - icon_ofs - text_buf->get_size() - Point2(_internal_margin[SIDE_RIGHT] - _internal_margin[SIDE_LEFT], 0)) / 2.0;
305
306 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_TOP) {
307 text_ofs.y += icon_region.size.height / 2;
308 }
309 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_BOTTOM) {
310 text_ofs.y -= icon_region.size.height / 2;
311 }
312
313 text_buf->set_alignment(align_rtl_checked);
314 text_buf->set_width(text_width);
315 switch (align_rtl_checked) {
316 case HORIZONTAL_ALIGNMENT_FILL:
317 case HORIZONTAL_ALIGNMENT_LEFT: {
318 if (icon_align_rtl_checked != HORIZONTAL_ALIGNMENT_LEFT) {
319 icon_ofs.x = 0.0;
320 }
321 if (_internal_margin[SIDE_LEFT] > 0) {
322 text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x + _internal_margin[SIDE_LEFT] + theme_cache.h_separation;
323 } else {
324 text_ofs.x = style->get_margin(SIDE_LEFT) + icon_ofs.x;
325 }
326 text_ofs.y += style->get_offset().y;
327 } break;
328 case HORIZONTAL_ALIGNMENT_CENTER: {
329 if (text_ofs.x < 0) {
330 text_ofs.x = 0;
331 }
332 if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_LEFT) {
333 text_ofs += icon_ofs;
334 }
335 text_ofs += style->get_offset();
336 } break;
337 case HORIZONTAL_ALIGNMENT_RIGHT: {
338 if (_internal_margin[SIDE_RIGHT] > 0) {
339 text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width - _internal_margin[SIDE_RIGHT] - theme_cache.h_separation;
340 } else {
341 text_ofs.x = size.x - style->get_margin(SIDE_RIGHT) - text_width;
342 }
343 text_ofs.y += style->get_offset().y;
344 if (icon_align_rtl_checked == HORIZONTAL_ALIGNMENT_RIGHT) {
345 text_ofs.x -= icon_ofs.x;
346 }
347 } break;
348 }
349
350 Color font_outline_color = theme_cache.font_outline_color;
351 int outline_size = theme_cache.outline_size;
352 if (outline_size > 0 && font_outline_color.a > 0) {
353 text_buf->draw_outline(ci, text_ofs, outline_size, font_outline_color);
354 }
355 text_buf->draw(ci, text_ofs, color);
356 } break;
357 }
358}
359
360Size2 Button::_fit_icon_size(const Size2 &p_size) const {
361 int max_width = theme_cache.icon_max_width;
362 Size2 icon_size = p_size;
363
364 if (max_width > 0 && icon_size.width > max_width) {
365 icon_size.height = icon_size.height * max_width / icon_size.width;
366 icon_size.width = max_width;
367 }
368
369 return icon_size;
370}
371
372Size2 Button::get_minimum_size_for_text_and_icon(const String &p_text, Ref<Texture2D> p_icon) const {
373 Ref<TextParagraph> paragraph;
374 if (p_text.is_empty()) {
375 paragraph = text_buf;
376 } else {
377 paragraph.instantiate();
378 const_cast<Button *>(this)->_shape(paragraph, p_text);
379 }
380
381 Size2 minsize = paragraph->get_size();
382 if (clip_text || overrun_behavior != TextServer::OVERRUN_NO_TRIMMING) {
383 minsize.width = 0;
384 }
385
386 if (!expand_icon && p_icon.is_valid()) {
387 Size2 icon_size = _fit_icon_size(p_icon->get_size());
388 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_CENTER) {
389 minsize.height = MAX(minsize.height, icon_size.height);
390 } else {
391 minsize.height += icon_size.height;
392 }
393
394 if (horizontal_icon_alignment != HORIZONTAL_ALIGNMENT_CENTER) {
395 minsize.width += icon_size.width;
396 if (!xl_text.is_empty() || !p_text.is_empty()) {
397 minsize.width += MAX(0, theme_cache.h_separation);
398 }
399 } else {
400 minsize.width = MAX(minsize.width, icon_size.width);
401 }
402 }
403
404 if (!xl_text.is_empty() || !p_text.is_empty()) {
405 Ref<Font> font = theme_cache.font;
406 float font_height = font->get_height(theme_cache.font_size);
407 if (vertical_icon_alignment == VERTICAL_ALIGNMENT_CENTER) {
408 minsize.height = MAX(font_height, minsize.height);
409 } else {
410 minsize.height += font_height;
411 }
412 }
413
414 return theme_cache.normal->get_minimum_size() + minsize;
415}
416
417void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) {
418 if (p_paragraph.is_null()) {
419 p_paragraph = text_buf;
420 }
421
422 if (p_text.is_empty()) {
423 p_text = xl_text;
424 }
425
426 p_paragraph->clear();
427
428 Ref<Font> font = theme_cache.font;
429 int font_size = theme_cache.font_size;
430 if (font.is_null() || font_size == 0) {
431 // Can't shape without a valid font and a non-zero size.
432 return;
433 }
434
435 if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
436 p_paragraph->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
437 } else {
438 p_paragraph->set_direction((TextServer::Direction)text_direction);
439 }
440 p_paragraph->add_string(p_text, font, font_size, language);
441 p_paragraph->set_text_overrun_behavior(overrun_behavior);
442}
443
444void Button::set_text_overrun_behavior(TextServer::OverrunBehavior p_behavior) {
445 if (overrun_behavior != p_behavior) {
446 overrun_behavior = p_behavior;
447 _shape();
448
449 queue_redraw();
450 update_minimum_size();
451 }
452}
453
454TextServer::OverrunBehavior Button::get_text_overrun_behavior() const {
455 return overrun_behavior;
456}
457
458void Button::set_text(const String &p_text) {
459 if (text != p_text) {
460 text = p_text;
461 xl_text = atr(text);
462 _shape();
463
464 queue_redraw();
465 update_minimum_size();
466 }
467}
468
469String Button::get_text() const {
470 return text;
471}
472
473void Button::set_text_direction(Control::TextDirection p_text_direction) {
474 ERR_FAIL_COND((int)p_text_direction < -1 || (int)p_text_direction > 3);
475 if (text_direction != p_text_direction) {
476 text_direction = p_text_direction;
477 _shape();
478 queue_redraw();
479 }
480}
481
482Control::TextDirection Button::get_text_direction() const {
483 return text_direction;
484}
485
486void Button::set_language(const String &p_language) {
487 if (language != p_language) {
488 language = p_language;
489 _shape();
490 queue_redraw();
491 }
492}
493
494String Button::get_language() const {
495 return language;
496}
497
498void Button::set_icon(const Ref<Texture2D> &p_icon) {
499 if (icon == p_icon) {
500 return;
501 }
502
503 if (icon.is_valid()) {
504 icon->disconnect_changed(callable_mp(this, &Button::_texture_changed));
505 }
506
507 icon = p_icon;
508
509 if (icon.is_valid()) {
510 icon->connect_changed(callable_mp(this, &Button::_texture_changed));
511 }
512
513 queue_redraw();
514 update_minimum_size();
515}
516
517void Button::_texture_changed() {
518 queue_redraw();
519 update_minimum_size();
520}
521
522Ref<Texture2D> Button::get_icon() const {
523 return icon;
524}
525
526void Button::set_expand_icon(bool p_enabled) {
527 if (expand_icon != p_enabled) {
528 expand_icon = p_enabled;
529 _queue_update_size_cache();
530 queue_redraw();
531 update_minimum_size();
532 }
533}
534
535bool Button::is_expand_icon() const {
536 return expand_icon;
537}
538
539void Button::set_flat(bool p_enabled) {
540 if (flat != p_enabled) {
541 flat = p_enabled;
542 queue_redraw();
543 }
544}
545
546bool Button::is_flat() const {
547 return flat;
548}
549
550void Button::set_clip_text(bool p_enabled) {
551 if (clip_text != p_enabled) {
552 clip_text = p_enabled;
553 queue_redraw();
554 update_minimum_size();
555 }
556}
557
558bool Button::get_clip_text() const {
559 return clip_text;
560}
561
562void Button::set_text_alignment(HorizontalAlignment p_alignment) {
563 if (alignment != p_alignment) {
564 alignment = p_alignment;
565 queue_redraw();
566 }
567}
568
569HorizontalAlignment Button::get_text_alignment() const {
570 return alignment;
571}
572
573void Button::set_icon_alignment(HorizontalAlignment p_alignment) {
574 horizontal_icon_alignment = p_alignment;
575 update_minimum_size();
576 queue_redraw();
577}
578
579void Button::set_vertical_icon_alignment(VerticalAlignment p_alignment) {
580 vertical_icon_alignment = p_alignment;
581 update_minimum_size();
582 queue_redraw();
583}
584
585HorizontalAlignment Button::get_icon_alignment() const {
586 return horizontal_icon_alignment;
587}
588
589VerticalAlignment Button::get_vertical_icon_alignment() const {
590 return vertical_icon_alignment;
591}
592
593void Button::_bind_methods() {
594 ClassDB::bind_method(D_METHOD("set_text", "text"), &Button::set_text);
595 ClassDB::bind_method(D_METHOD("get_text"), &Button::get_text);
596 ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "overrun_behavior"), &Button::set_text_overrun_behavior);
597 ClassDB::bind_method(D_METHOD("get_text_overrun_behavior"), &Button::get_text_overrun_behavior);
598 ClassDB::bind_method(D_METHOD("set_text_direction", "direction"), &Button::set_text_direction);
599 ClassDB::bind_method(D_METHOD("get_text_direction"), &Button::get_text_direction);
600 ClassDB::bind_method(D_METHOD("set_language", "language"), &Button::set_language);
601 ClassDB::bind_method(D_METHOD("get_language"), &Button::get_language);
602 ClassDB::bind_method(D_METHOD("set_button_icon", "texture"), &Button::set_icon);
603 ClassDB::bind_method(D_METHOD("get_button_icon"), &Button::get_icon);
604 ClassDB::bind_method(D_METHOD("set_flat", "enabled"), &Button::set_flat);
605 ClassDB::bind_method(D_METHOD("is_flat"), &Button::is_flat);
606 ClassDB::bind_method(D_METHOD("set_clip_text", "enabled"), &Button::set_clip_text);
607 ClassDB::bind_method(D_METHOD("get_clip_text"), &Button::get_clip_text);
608 ClassDB::bind_method(D_METHOD("set_text_alignment", "alignment"), &Button::set_text_alignment);
609 ClassDB::bind_method(D_METHOD("get_text_alignment"), &Button::get_text_alignment);
610 ClassDB::bind_method(D_METHOD("set_icon_alignment", "icon_alignment"), &Button::set_icon_alignment);
611 ClassDB::bind_method(D_METHOD("get_icon_alignment"), &Button::get_icon_alignment);
612 ClassDB::bind_method(D_METHOD("set_vertical_icon_alignment", "vertical_icon_alignment"), &Button::set_vertical_icon_alignment);
613 ClassDB::bind_method(D_METHOD("get_vertical_icon_alignment"), &Button::get_vertical_icon_alignment);
614 ClassDB::bind_method(D_METHOD("set_expand_icon", "enabled"), &Button::set_expand_icon);
615 ClassDB::bind_method(D_METHOD("is_expand_icon"), &Button::is_expand_icon);
616
617 ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
618 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
619 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
620
621 ADD_GROUP("Text Behavior", "");
622 ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_text_alignment", "get_text_alignment");
623 ADD_PROPERTY(PropertyInfo(Variant::INT, "text_overrun_behavior", PROPERTY_HINT_ENUM, "Trim Nothing,Trim Characters,Trim Words,Ellipsis,Word Ellipsis"), "set_text_overrun_behavior", "get_text_overrun_behavior");
624 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_text"), "set_clip_text", "get_clip_text");
625
626 ADD_GROUP("Icon Behavior", "");
627 ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_icon_alignment", "get_icon_alignment");
628 ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_icon_alignment", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_icon_alignment", "get_vertical_icon_alignment");
629 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand_icon"), "set_expand_icon", "is_expand_icon");
630
631 ADD_GROUP("BiDi", "");
632 ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
633 ADD_PROPERTY(PropertyInfo(Variant::STRING, "language", PROPERTY_HINT_LOCALE_ID, ""), "set_language", "get_language");
634
635 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, normal);
636 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, normal_mirrored);
637 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, pressed);
638 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, pressed_mirrored);
639 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, hover);
640 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, hover_mirrored);
641 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, hover_pressed);
642 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, hover_pressed_mirrored);
643 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, disabled);
644 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, disabled_mirrored);
645 BIND_THEME_ITEM(Theme::DATA_TYPE_STYLEBOX, Button, focus);
646
647 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_color);
648 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_focus_color);
649 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_pressed_color);
650 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_hover_color);
651 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_hover_pressed_color);
652 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_disabled_color);
653
654 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT, Button, font);
655 BIND_THEME_ITEM(Theme::DATA_TYPE_FONT_SIZE, Button, font_size);
656 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, outline_size);
657 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, font_outline_color);
658
659 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_normal_color);
660 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_focus_color);
661 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_pressed_color);
662 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_hover_color);
663 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_hover_pressed_color);
664 BIND_THEME_ITEM(Theme::DATA_TYPE_COLOR, Button, icon_disabled_color);
665
666 BIND_THEME_ITEM(Theme::DATA_TYPE_ICON, Button, icon);
667
668 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, h_separation);
669 BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, Button, icon_max_width);
670}
671
672Button::Button(const String &p_text) {
673 text_buf.instantiate();
674 text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_EDGE_SPACES);
675 set_mouse_filter(MOUSE_FILTER_STOP);
676
677 set_text(p_text);
678}
679
680Button::~Button() {
681}
682