1 /*************************************************************************/
2 /* screen_button.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 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 #include "screen_button.h"
31 #include "input_map.h"
32 #include "os/input.h"
33 #include "os/os.h"
34
set_texture(const Ref<Texture> & p_texture)35 void TouchScreenButton::set_texture(const Ref<Texture> &p_texture) {
36
37 texture = p_texture;
38 update();
39 }
40
get_texture() const41 Ref<Texture> TouchScreenButton::get_texture() const {
42
43 return texture;
44 }
45
set_texture_pressed(const Ref<Texture> & p_texture_pressed)46 void TouchScreenButton::set_texture_pressed(const Ref<Texture> &p_texture_pressed) {
47
48 texture_pressed = p_texture_pressed;
49 update();
50 }
51
get_texture_pressed() const52 Ref<Texture> TouchScreenButton::get_texture_pressed() const {
53
54 return texture_pressed;
55 }
56
set_bitmask(const Ref<BitMap> & p_bitmask)57 void TouchScreenButton::set_bitmask(const Ref<BitMap> &p_bitmask) {
58
59 bitmask = p_bitmask;
60 }
61
get_bitmask() const62 Ref<BitMap> TouchScreenButton::get_bitmask() const {
63
64 return bitmask;
65 }
66
set_shape(const Ref<Shape2D> & p_shape)67 void TouchScreenButton::set_shape(const Ref<Shape2D> &p_shape) {
68
69 if (shape.is_valid())
70 shape->disconnect("changed", this, "update");
71
72 shape = p_shape;
73
74 if (shape.is_valid())
75 shape->connect("changed", this, "update");
76
77 update();
78 }
79
get_shape() const80 Ref<Shape2D> TouchScreenButton::get_shape() const {
81
82 return shape;
83 }
84
set_shape_centered(bool p_shape_centered)85 void TouchScreenButton::set_shape_centered(bool p_shape_centered) {
86
87 shape_centered = p_shape_centered;
88 update();
89 }
90
is_shape_visible() const91 bool TouchScreenButton::is_shape_visible() const {
92
93 return shape_visible;
94 }
95
set_shape_visible(bool p_shape_visible)96 void TouchScreenButton::set_shape_visible(bool p_shape_visible) {
97
98 shape_visible = p_shape_visible;
99 update();
100 }
101
is_shape_centered() const102 bool TouchScreenButton::is_shape_centered() const {
103
104 return shape_centered;
105 }
106
_notification(int p_what)107 void TouchScreenButton::_notification(int p_what) {
108
109 switch (p_what) {
110
111 case NOTIFICATION_DRAW: {
112
113 if (!is_inside_tree())
114 return;
115 if (!get_tree()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY)
116 return;
117
118 if (finger_pressed != -1) {
119
120 if (texture_pressed.is_valid())
121 draw_texture(texture_pressed, Point2());
122 else if (texture.is_valid())
123 draw_texture(texture, Point2());
124
125 } else {
126 if (texture.is_valid())
127 draw_texture(texture, Point2());
128 }
129
130 if (!shape_visible)
131 return;
132 if (!get_tree()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint())
133 return;
134 if (shape.is_valid()) {
135 Color draw_col = get_tree()->get_debug_collisions_color();
136 Vector2 pos = shape_centered ? get_item_rect().size * 0.5f : Vector2();
137 draw_set_transform_matrix(get_canvas_transform().translated(pos));
138 shape->draw(get_canvas_item(), draw_col);
139 }
140
141 } break;
142 case NOTIFICATION_ENTER_TREE: {
143
144 if (!get_tree()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility == VISIBILITY_TOUCHSCREEN_ONLY)
145 return;
146 update();
147
148 if (!get_tree()->is_editor_hint())
149 set_process_input(is_visible());
150
151 if (action.operator String() != "" && InputMap::get_singleton()->has_action(action)) {
152 action_id = InputMap::get_singleton()->get_action_id(action);
153 } else {
154 action_id = -1;
155 }
156 } break;
157 case NOTIFICATION_EXIT_TREE: {
158 if (is_pressed())
159 _release(true);
160 } break;
161 case NOTIFICATION_VISIBILITY_CHANGED: {
162 if (get_tree()->is_editor_hint())
163 break;
164 if (is_visible()) {
165 set_process_input(true);
166 } else {
167 set_process_input(false);
168 if (is_pressed())
169 _release();
170 }
171 } break;
172 case NOTIFICATION_PAUSED: {
173 if (is_pressed())
174 _release();
175 } break;
176 }
177 }
178
is_pressed() const179 bool TouchScreenButton::is_pressed() const {
180
181 return finger_pressed != -1;
182 }
183
set_action(const String & p_action)184 void TouchScreenButton::set_action(const String &p_action) {
185
186 action = p_action;
187 if (action.operator String() != "" && InputMap::get_singleton()->has_action(action)) {
188 action_id = InputMap::get_singleton()->get_action_id(action);
189 } else {
190 action_id = -1;
191 }
192 }
193
get_action() const194 String TouchScreenButton::get_action() const {
195
196 return action;
197 }
198
_input(const InputEvent & p_event)199 void TouchScreenButton::_input(const InputEvent &p_event) {
200
201 if (!get_tree())
202 return;
203
204 if (p_event.device != 0)
205 return;
206
207 ERR_FAIL_COND(!is_visible());
208
209 if (passby_press) {
210
211 if (p_event.type == InputEvent::SCREEN_TOUCH && !p_event.screen_touch.pressed && finger_pressed == p_event.screen_touch.index) {
212
213 _release();
214 }
215
216 if ((p_event.type == InputEvent::SCREEN_TOUCH && p_event.screen_touch.pressed) || p_event.type == InputEvent::SCREEN_DRAG) {
217
218 if (finger_pressed == -1 || p_event.screen_touch.index == finger_pressed) {
219
220 if (_is_touch_inside(p_event.screen_touch)) {
221 if (finger_pressed == -1) {
222 _press(p_event.screen_touch.index);
223 }
224 } else {
225 if (finger_pressed != -1) {
226 _release();
227 }
228 }
229 }
230 }
231
232 } else {
233
234 if (p_event.type == InputEvent::SCREEN_TOUCH) {
235
236 if (p_event.screen_touch.pressed) {
237
238 const bool can_press = finger_pressed == -1;
239 if (!can_press)
240 return; //already fingering
241
242 if (_is_touch_inside(p_event.screen_touch)) {
243 _press(p_event.screen_touch.index);
244 }
245 } else {
246 if (p_event.screen_touch.index == finger_pressed) {
247 _release();
248 }
249 }
250 }
251 }
252 }
253
_is_touch_inside(const InputEventScreenTouch & p_touch)254 bool TouchScreenButton::_is_touch_inside(const InputEventScreenTouch &p_touch) {
255
256 Point2 coord = get_global_transform_with_canvas().affine_inverse().xform(Point2(p_touch.x, p_touch.y));
257
258 bool touched = false;
259 bool check_rect = true;
260
261 Rect2 item_rect = get_item_rect();
262
263 if (shape.is_valid()) {
264
265 check_rect = false;
266 Matrix32 xform = shape_centered ? Matrix32().translated(item_rect.size * 0.5f) : Matrix32();
267 touched = shape->collide(xform, unit_rect, Matrix32(0, coord + Vector2(0.5, 0.5)));
268 }
269
270 if (bitmask.is_valid()) {
271
272 check_rect = false;
273 if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) {
274
275 if (bitmask->get_bit(coord))
276 touched = true;
277 }
278 }
279
280 if (!touched && check_rect) {
281
282 if (texture.is_valid())
283 touched = item_rect.has_point(coord);
284 }
285
286 return touched;
287 }
288
_press(int p_finger_pressed)289 void TouchScreenButton::_press(int p_finger_pressed) {
290
291 finger_pressed = p_finger_pressed;
292
293 if (action_id != -1) {
294
295 Input::get_singleton()->action_press(action);
296 InputEvent ie;
297 ie.type = InputEvent::ACTION;
298 ie.ID = 0;
299 ie.action.action = action_id;
300 ie.action.pressed = true;
301 get_tree()->input_event(ie);
302 }
303
304 emit_signal("pressed");
305 update();
306 }
307
_release(bool p_exiting_tree)308 void TouchScreenButton::_release(bool p_exiting_tree) {
309
310 finger_pressed = -1;
311
312 if (action_id != -1) {
313
314 Input::get_singleton()->action_release(action);
315 if (!p_exiting_tree) {
316 InputEvent ie;
317 ie.type = InputEvent::ACTION;
318 ie.ID = 0;
319 ie.action.action = action_id;
320 ie.action.pressed = false;
321 get_tree()->input_event(ie);
322 }
323 }
324
325 if (!p_exiting_tree) {
326 emit_signal("released");
327 update();
328 }
329 }
330
get_item_rect() const331 Rect2 TouchScreenButton::get_item_rect() const {
332
333 if (texture.is_null())
334 return Rect2(0, 0, 1, 1);
335 //if (texture.is_null())
336 // return CanvasItem::get_item_rect();
337
338 return Rect2(Size2(), texture->get_size());
339 }
340
set_visibility_mode(VisibilityMode p_mode)341 void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
342 visibility = p_mode;
343 update();
344 }
345
get_visibility_mode() const346 TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const {
347
348 return visibility;
349 }
350
set_passby_press(bool p_enable)351 void TouchScreenButton::set_passby_press(bool p_enable) {
352
353 passby_press = p_enable;
354 }
355
is_passby_press_enabled() const356 bool TouchScreenButton::is_passby_press_enabled() const {
357
358 return passby_press;
359 }
360
_bind_methods()361 void TouchScreenButton::_bind_methods() {
362
363 ObjectTypeDB::bind_method(_MD("set_texture", "texture"), &TouchScreenButton::set_texture);
364 ObjectTypeDB::bind_method(_MD("get_texture"), &TouchScreenButton::get_texture);
365
366 ObjectTypeDB::bind_method(_MD("set_texture_pressed", "texture_pressed"), &TouchScreenButton::set_texture_pressed);
367 ObjectTypeDB::bind_method(_MD("get_texture_pressed"), &TouchScreenButton::get_texture_pressed);
368
369 ObjectTypeDB::bind_method(_MD("set_bitmask", "bitmask"), &TouchScreenButton::set_bitmask);
370 ObjectTypeDB::bind_method(_MD("get_bitmask"), &TouchScreenButton::get_bitmask);
371
372 ObjectTypeDB::bind_method(_MD("set_shape", "shape"), &TouchScreenButton::set_shape);
373 ObjectTypeDB::bind_method(_MD("get_shape"), &TouchScreenButton::get_shape);
374
375 ObjectTypeDB::bind_method(_MD("set_shape_centered", "bool"), &TouchScreenButton::set_shape_centered);
376 ObjectTypeDB::bind_method(_MD("is_shape_centered"), &TouchScreenButton::is_shape_centered);
377
378 ObjectTypeDB::bind_method(_MD("set_shape_visible", "bool"), &TouchScreenButton::set_shape_visible);
379 ObjectTypeDB::bind_method(_MD("is_shape_visible"), &TouchScreenButton::is_shape_visible);
380
381 ObjectTypeDB::bind_method(_MD("set_action", "action"), &TouchScreenButton::set_action);
382 ObjectTypeDB::bind_method(_MD("get_action"), &TouchScreenButton::get_action);
383
384 ObjectTypeDB::bind_method(_MD("set_visibility_mode", "mode"), &TouchScreenButton::set_visibility_mode);
385 ObjectTypeDB::bind_method(_MD("get_visibility_mode"), &TouchScreenButton::get_visibility_mode);
386
387 ObjectTypeDB::bind_method(_MD("set_passby_press", "enabled"), &TouchScreenButton::set_passby_press);
388 ObjectTypeDB::bind_method(_MD("is_passby_press_enabled"), &TouchScreenButton::is_passby_press_enabled);
389
390 ObjectTypeDB::bind_method(_MD("is_pressed"), &TouchScreenButton::is_pressed);
391
392 ObjectTypeDB::bind_method(_MD("_input"), &TouchScreenButton::_input);
393
394 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"), _SCS("get_texture"));
395 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture_pressed"), _SCS("get_texture_pressed"));
396 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "bitmask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), _SCS("set_bitmask"), _SCS("get_bitmask"));
397 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), _SCS("set_shape"), _SCS("get_shape"));
398 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), _SCS("set_shape_centered"), _SCS("is_shape_centered"));
399 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_visible"), _SCS("set_shape_visible"), _SCS("is_shape_visible"));
400 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "passby_press"), _SCS("set_passby_press"), _SCS("is_passby_press_enabled"));
401 ADD_PROPERTY(PropertyInfo(Variant::STRING, "action"), _SCS("set_action"), _SCS("get_action"));
402 ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Always,TouchScreen Only"), _SCS("set_visibility_mode"), _SCS("get_visibility_mode"));
403
404 ADD_SIGNAL(MethodInfo("pressed"));
405 ADD_SIGNAL(MethodInfo("released"));
406 }
407
TouchScreenButton()408 TouchScreenButton::TouchScreenButton() {
409
410 finger_pressed = -1;
411 action_id = -1;
412 passby_press = false;
413 visibility = VISIBILITY_ALWAYS;
414 shape_centered = true;
415 shape_visible = true;
416 unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D));
417 unit_rect->set_extents(Vector2(0.5, 0.5));
418 }
419