1 /*************************************************************************/
2 /* texture_button.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
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 "texture_button.h"
32 #include "core/typedefs.h"
33 #include <stdlib.h>
34
get_minimum_size() const35 Size2 TextureButton::get_minimum_size() const {
36
37 Size2 rscale = Control::get_minimum_size();
38
39 if (!expand) {
40 if (normal.is_null()) {
41 if (pressed.is_null()) {
42 if (hover.is_null())
43 if (click_mask.is_null())
44 rscale = Size2();
45 else
46 rscale = click_mask->get_size();
47 else
48 rscale = hover->get_size();
49 } else
50 rscale = pressed->get_size();
51
52 } else
53 rscale = normal->get_size();
54 }
55
56 return rscale.abs();
57 }
58
has_point(const Point2 & p_point) const59 bool TextureButton::has_point(const Point2 &p_point) const {
60
61 if (click_mask.is_valid()) {
62
63 Point2 point = p_point;
64 Rect2 rect = Rect2();
65 Size2 mask_size = click_mask->get_size();
66
67 if (_position_rect.has_no_area()) {
68 rect.size = mask_size;
69 } else if (_tile) {
70 // if the stretch mode is tile we offset the point to keep it inside the mask size
71 rect.size = mask_size;
72 if (_position_rect.has_point(point)) {
73 int cols = (int)Math::ceil(_position_rect.size.x / mask_size.x);
74 int rows = (int)Math::ceil(_position_rect.size.y / mask_size.y);
75 int col = (int)(point.x / mask_size.x) % cols;
76 int row = (int)(point.y / mask_size.y) % rows;
77 point.x -= mask_size.x * col;
78 point.y -= mask_size.y * row;
79 }
80 } else {
81 // we need to transform the point from our scaled / translated image back to our mask image
82 Point2 ofs = _position_rect.position;
83 Size2 scale = mask_size / _position_rect.size;
84
85 switch (stretch_mode) {
86 case STRETCH_KEEP_ASPECT_COVERED: {
87 // if the stretch mode is aspect covered the image uses a texture region so we need to take that into account
88 float min = MIN(scale.x, scale.y);
89 scale.x = min;
90 scale.y = min;
91 ofs -= _texture_region.position / min;
92 } break;
93 default: {
94 // FIXME: Why a switch if we only handle one enum value?
95 }
96 }
97
98 // offset and scale the new point position to adjust it to the bitmask size
99 point -= ofs;
100 point *= scale;
101
102 // finally, we need to check if the point is inside a rectangle with a position >= 0,0 and a size <= mask_size
103 rect.position = Point2(MAX(0, _texture_region.position.x), MAX(0, _texture_region.position.y));
104 rect.size = Size2(MIN(mask_size.x, _texture_region.size.x), MIN(mask_size.y, _texture_region.size.y));
105 }
106
107 if (!rect.has_point(point)) {
108 return false;
109 }
110
111 Point2i p = point;
112 return click_mask->get_bit(p);
113 }
114
115 return Control::has_point(p_point);
116 }
117
_notification(int p_what)118 void TextureButton::_notification(int p_what) {
119
120 switch (p_what) {
121
122 case NOTIFICATION_DRAW: {
123 DrawMode draw_mode = get_draw_mode();
124
125 Ref<Texture> texdraw;
126
127 switch (draw_mode) {
128 case DRAW_NORMAL: {
129
130 if (normal.is_valid())
131 texdraw = normal;
132 } break;
133 case DRAW_HOVER_PRESSED:
134 case DRAW_PRESSED: {
135
136 if (pressed.is_null()) {
137 if (hover.is_null()) {
138 if (normal.is_valid())
139 texdraw = normal;
140 } else
141 texdraw = hover;
142
143 } else
144 texdraw = pressed;
145 } break;
146 case DRAW_HOVER: {
147
148 if (hover.is_null()) {
149 if (pressed.is_valid() && is_pressed())
150 texdraw = pressed;
151 else if (normal.is_valid())
152 texdraw = normal;
153 } else
154 texdraw = hover;
155 } break;
156 case DRAW_DISABLED: {
157
158 if (disabled.is_null()) {
159 if (normal.is_valid())
160 texdraw = normal;
161 } else
162 texdraw = disabled;
163 } break;
164 }
165
166 if (texdraw.is_valid()) {
167 Point2 ofs;
168 Size2 size = texdraw->get_size();
169 _texture_region = Rect2(Point2(), texdraw->get_size());
170 _tile = false;
171 if (expand) {
172 switch (stretch_mode) {
173 case STRETCH_KEEP:
174 size = texdraw->get_size();
175 break;
176 case STRETCH_SCALE:
177 size = get_size();
178 break;
179 case STRETCH_TILE:
180 size = get_size();
181 _tile = true;
182 break;
183 case STRETCH_KEEP_CENTERED:
184 ofs = (get_size() - texdraw->get_size()) / 2;
185 size = texdraw->get_size();
186 break;
187 case STRETCH_KEEP_ASPECT_CENTERED:
188 case STRETCH_KEEP_ASPECT: {
189 Size2 _size = get_size();
190 float tex_width = texdraw->get_width() * _size.height / texdraw->get_height();
191 float tex_height = _size.height;
192
193 if (tex_width > _size.width) {
194 tex_width = _size.width;
195 tex_height = texdraw->get_height() * tex_width / texdraw->get_width();
196 }
197
198 if (stretch_mode == STRETCH_KEEP_ASPECT_CENTERED) {
199 ofs.x = (_size.width - tex_width) / 2;
200 ofs.y = (_size.height - tex_height) / 2;
201 }
202 size.width = tex_width;
203 size.height = tex_height;
204 } break;
205 case STRETCH_KEEP_ASPECT_COVERED: {
206 size = get_size();
207 Size2 tex_size = texdraw->get_size();
208 Size2 scale_size(size.width / tex_size.width, size.height / tex_size.height);
209 float scale = scale_size.width > scale_size.height ? scale_size.width : scale_size.height;
210 Size2 scaled_tex_size = tex_size * scale;
211 Point2 ofs2 = ((scaled_tex_size - size) / scale).abs() / 2.0f;
212 _texture_region = Rect2(ofs2, size / scale);
213 } break;
214 }
215 }
216
217 _position_rect = Rect2(ofs, size);
218 if (_tile) {
219 draw_texture_rect(texdraw, _position_rect, _tile);
220 } else {
221 draw_texture_rect_region(texdraw, _position_rect, _texture_region);
222 }
223 } else {
224 _position_rect = Rect2();
225 }
226
227 if (has_focus() && focused.is_valid()) {
228 draw_texture_rect(focused, _position_rect, false);
229 };
230 } break;
231 }
232 }
233
_bind_methods()234 void TextureButton::_bind_methods() {
235
236 ClassDB::bind_method(D_METHOD("set_normal_texture", "texture"), &TextureButton::set_normal_texture);
237 ClassDB::bind_method(D_METHOD("set_pressed_texture", "texture"), &TextureButton::set_pressed_texture);
238 ClassDB::bind_method(D_METHOD("set_hover_texture", "texture"), &TextureButton::set_hover_texture);
239 ClassDB::bind_method(D_METHOD("set_disabled_texture", "texture"), &TextureButton::set_disabled_texture);
240 ClassDB::bind_method(D_METHOD("set_focused_texture", "texture"), &TextureButton::set_focused_texture);
241 ClassDB::bind_method(D_METHOD("set_click_mask", "mask"), &TextureButton::set_click_mask);
242 ClassDB::bind_method(D_METHOD("set_expand", "p_expand"), &TextureButton::set_expand);
243 ClassDB::bind_method(D_METHOD("set_stretch_mode", "p_mode"), &TextureButton::set_stretch_mode);
244
245 ClassDB::bind_method(D_METHOD("get_normal_texture"), &TextureButton::get_normal_texture);
246 ClassDB::bind_method(D_METHOD("get_pressed_texture"), &TextureButton::get_pressed_texture);
247 ClassDB::bind_method(D_METHOD("get_hover_texture"), &TextureButton::get_hover_texture);
248 ClassDB::bind_method(D_METHOD("get_disabled_texture"), &TextureButton::get_disabled_texture);
249 ClassDB::bind_method(D_METHOD("get_focused_texture"), &TextureButton::get_focused_texture);
250 ClassDB::bind_method(D_METHOD("get_click_mask"), &TextureButton::get_click_mask);
251 ClassDB::bind_method(D_METHOD("get_expand"), &TextureButton::get_expand);
252 ClassDB::bind_method(D_METHOD("get_stretch_mode"), &TextureButton::get_stretch_mode);
253
254 ADD_GROUP("Textures", "texture_");
255 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_normal_texture", "get_normal_texture");
256 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_pressed_texture", "get_pressed_texture");
257 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_hover", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_hover_texture", "get_hover_texture");
258 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_disabled", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_disabled_texture", "get_disabled_texture");
259 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_focused", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_focused_texture", "get_focused_texture");
260 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_click_mask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_click_mask", "get_click_mask");
261 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "expand", PROPERTY_HINT_RESOURCE_TYPE, "bool"), "set_expand", "get_expand");
262 ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Scale,Tile,Keep,Keep Centered,Keep Aspect,Keep Aspect Centered,Keep Aspect Covered"), "set_stretch_mode", "get_stretch_mode");
263
264 BIND_ENUM_CONSTANT(STRETCH_SCALE);
265 BIND_ENUM_CONSTANT(STRETCH_TILE);
266 BIND_ENUM_CONSTANT(STRETCH_KEEP);
267 BIND_ENUM_CONSTANT(STRETCH_KEEP_CENTERED);
268 BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT);
269 BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT_CENTERED);
270 BIND_ENUM_CONSTANT(STRETCH_KEEP_ASPECT_COVERED);
271 }
272
set_normal_texture(const Ref<Texture> & p_normal)273 void TextureButton::set_normal_texture(const Ref<Texture> &p_normal) {
274
275 normal = p_normal;
276 update();
277 minimum_size_changed();
278 }
279
set_pressed_texture(const Ref<Texture> & p_pressed)280 void TextureButton::set_pressed_texture(const Ref<Texture> &p_pressed) {
281
282 pressed = p_pressed;
283 update();
284 }
set_hover_texture(const Ref<Texture> & p_hover)285 void TextureButton::set_hover_texture(const Ref<Texture> &p_hover) {
286
287 hover = p_hover;
288 update();
289 }
set_disabled_texture(const Ref<Texture> & p_disabled)290 void TextureButton::set_disabled_texture(const Ref<Texture> &p_disabled) {
291
292 disabled = p_disabled;
293 update();
294 }
set_click_mask(const Ref<BitMap> & p_click_mask)295 void TextureButton::set_click_mask(const Ref<BitMap> &p_click_mask) {
296
297 click_mask = p_click_mask;
298 update();
299 }
300
get_normal_texture() const301 Ref<Texture> TextureButton::get_normal_texture() const {
302
303 return normal;
304 }
get_pressed_texture() const305 Ref<Texture> TextureButton::get_pressed_texture() const {
306
307 return pressed;
308 }
get_hover_texture() const309 Ref<Texture> TextureButton::get_hover_texture() const {
310
311 return hover;
312 }
get_disabled_texture() const313 Ref<Texture> TextureButton::get_disabled_texture() const {
314
315 return disabled;
316 }
get_click_mask() const317 Ref<BitMap> TextureButton::get_click_mask() const {
318
319 return click_mask;
320 }
321
get_focused_texture() const322 Ref<Texture> TextureButton::get_focused_texture() const {
323
324 return focused;
325 };
326
set_focused_texture(const Ref<Texture> & p_focused)327 void TextureButton::set_focused_texture(const Ref<Texture> &p_focused) {
328
329 focused = p_focused;
330 };
331
get_expand() const332 bool TextureButton::get_expand() const {
333 return expand;
334 }
335
set_expand(bool p_expand)336 void TextureButton::set_expand(bool p_expand) {
337 expand = p_expand;
338 minimum_size_changed();
339 update();
340 }
341
set_stretch_mode(StretchMode p_stretch_mode)342 void TextureButton::set_stretch_mode(StretchMode p_stretch_mode) {
343 stretch_mode = p_stretch_mode;
344 update();
345 }
346
get_stretch_mode() const347 TextureButton::StretchMode TextureButton::get_stretch_mode() const {
348 return stretch_mode;
349 }
350
TextureButton()351 TextureButton::TextureButton() {
352 expand = false;
353 stretch_mode = STRETCH_SCALE;
354
355 _texture_region = Rect2();
356 _position_rect = Rect2();
357 _tile = false;
358 }
359