1/**************************************************************************/
2/* touch_screen_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 "touch_screen_button.h"
32
33#include "scene/main/window.h"
34#include "scene/scene_string_names.h"
35
36void TouchScreenButton::set_texture_normal(const Ref<Texture2D> &p_texture) {
37 if (texture_normal == p_texture) {
38 return;
39 }
40 if (texture_normal.is_valid()) {
41 texture_normal->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
42 }
43 texture_normal = p_texture;
44 if (texture_normal.is_valid()) {
45 texture_normal->connect(SceneStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw), CONNECT_REFERENCE_COUNTED);
46 }
47 queue_redraw();
48}
49
50Ref<Texture2D> TouchScreenButton::get_texture_normal() const {
51 return texture_normal;
52}
53
54void TouchScreenButton::set_texture_pressed(const Ref<Texture2D> &p_texture_pressed) {
55 if (texture_pressed == p_texture_pressed) {
56 return;
57 }
58 if (texture_pressed.is_valid()) {
59 texture_pressed->disconnect(SceneStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
60 }
61 texture_pressed = p_texture_pressed;
62 if (texture_pressed.is_valid()) {
63 texture_pressed->connect(SceneStringNames::get_singleton()->changed, callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw), CONNECT_REFERENCE_COUNTED);
64 }
65 queue_redraw();
66}
67
68Ref<Texture2D> TouchScreenButton::get_texture_pressed() const {
69 return texture_pressed;
70}
71
72void TouchScreenButton::set_bitmask(const Ref<BitMap> &p_bitmask) {
73 bitmask = p_bitmask;
74}
75
76Ref<BitMap> TouchScreenButton::get_bitmask() const {
77 return bitmask;
78}
79
80void TouchScreenButton::set_shape(const Ref<Shape2D> &p_shape) {
81 if (shape == p_shape) {
82 return;
83 }
84 if (shape.is_valid()) {
85 shape->disconnect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
86 }
87 shape = p_shape;
88 if (shape.is_valid()) {
89 shape->connect_changed(callable_mp((CanvasItem *)this, &CanvasItem::queue_redraw));
90 }
91 queue_redraw();
92}
93
94Ref<Shape2D> TouchScreenButton::get_shape() const {
95 return shape;
96}
97
98void TouchScreenButton::set_shape_centered(bool p_shape_centered) {
99 shape_centered = p_shape_centered;
100 queue_redraw();
101}
102
103bool TouchScreenButton::is_shape_visible() const {
104 return shape_visible;
105}
106
107void TouchScreenButton::set_shape_visible(bool p_shape_visible) {
108 shape_visible = p_shape_visible;
109 queue_redraw();
110}
111
112bool TouchScreenButton::is_shape_centered() const {
113 return shape_centered;
114}
115
116void TouchScreenButton::_notification(int p_what) {
117 switch (p_what) {
118 case NOTIFICATION_DRAW: {
119 if (!is_inside_tree()) {
120 return;
121 }
122 if (!Engine::get_singleton()->is_editor_hint() && !DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) {
123 return;
124 }
125
126 if (finger_pressed != -1) {
127 if (texture_pressed.is_valid()) {
128 draw_texture(texture_pressed, Point2());
129 } else if (texture_normal.is_valid()) {
130 draw_texture(texture_normal, Point2());
131 }
132
133 } else {
134 if (texture_normal.is_valid()) {
135 draw_texture(texture_normal, Point2());
136 }
137 }
138
139 if (!shape_visible) {
140 return;
141 }
142 if (!Engine::get_singleton()->is_editor_hint() && !get_tree()->is_debugging_collisions_hint()) {
143 return;
144 }
145 if (shape.is_valid()) {
146 Color draw_col = get_tree()->get_debug_collisions_color();
147
148 Vector2 pos;
149 if (shape_centered && texture_normal.is_valid()) {
150 pos = texture_normal->get_size() * 0.5;
151 }
152
153 draw_set_transform_matrix(get_canvas_transform().translated_local(pos));
154 shape->draw(get_canvas_item(), draw_col);
155 }
156 } break;
157
158 case NOTIFICATION_ENTER_TREE: {
159 if (!Engine::get_singleton()->is_editor_hint() && !DisplayServer::get_singleton()->is_touchscreen_available() && visibility == VISIBILITY_TOUCHSCREEN_ONLY) {
160 return;
161 }
162 queue_redraw();
163
164 if (!Engine::get_singleton()->is_editor_hint()) {
165 set_process_input(is_visible_in_tree());
166 }
167 } break;
168
169 case NOTIFICATION_EXIT_TREE: {
170 if (is_pressed()) {
171 _release(true);
172 }
173 } break;
174
175 case NOTIFICATION_VISIBILITY_CHANGED: {
176 if (Engine::get_singleton()->is_editor_hint()) {
177 break;
178 }
179 if (is_visible_in_tree()) {
180 set_process_input(true);
181 } else {
182 set_process_input(false);
183 if (is_pressed()) {
184 _release();
185 }
186 }
187 } break;
188
189 case NOTIFICATION_PAUSED: {
190 if (is_pressed()) {
191 _release();
192 }
193 } break;
194 }
195}
196
197bool TouchScreenButton::is_pressed() const {
198 return finger_pressed != -1;
199}
200
201void TouchScreenButton::set_action(const String &p_action) {
202 action = p_action;
203}
204
205String TouchScreenButton::get_action() const {
206 return action;
207}
208
209void TouchScreenButton::input(const Ref<InputEvent> &p_event) {
210 ERR_FAIL_COND(p_event.is_null());
211
212 if (!is_visible_in_tree()) {
213 return;
214 }
215
216 const InputEventScreenTouch *st = Object::cast_to<InputEventScreenTouch>(*p_event);
217
218 if (passby_press) {
219 const InputEventScreenDrag *sd = Object::cast_to<InputEventScreenDrag>(*p_event);
220
221 if (st && !st->is_pressed() && finger_pressed == st->get_index()) {
222 _release();
223 }
224
225 if ((st && st->is_pressed()) || sd) {
226 int index = st ? st->get_index() : sd->get_index();
227 Point2 coord = st ? st->get_position() : sd->get_position();
228
229 if (finger_pressed == -1 || index == finger_pressed) {
230 if (_is_point_inside(coord)) {
231 if (finger_pressed == -1) {
232 _press(index);
233 }
234 } else {
235 if (finger_pressed != -1) {
236 _release();
237 }
238 }
239 }
240 }
241
242 } else {
243 if (st) {
244 if (st->is_pressed()) {
245 const bool can_press = finger_pressed == -1;
246 if (!can_press) {
247 return; //already fingering
248 }
249
250 if (_is_point_inside(st->get_position())) {
251 _press(st->get_index());
252 }
253 } else {
254 if (st->get_index() == finger_pressed) {
255 _release();
256 }
257 }
258 }
259 }
260}
261
262bool TouchScreenButton::_is_point_inside(const Point2 &p_point) {
263 Point2 coord = (get_global_transform_with_canvas()).affine_inverse().xform(p_point);
264
265 bool touched = false;
266 bool check_rect = true;
267
268 if (shape.is_valid()) {
269 check_rect = false;
270
271 Vector2 pos;
272 if (shape_centered && texture_normal.is_valid()) {
273 pos = texture_normal->get_size() * 0.5;
274 }
275
276 touched = shape->collide(Transform2D().translated_local(pos), unit_rect, Transform2D(0, coord + Vector2(0.5, 0.5)));
277 }
278
279 if (bitmask.is_valid()) {
280 check_rect = false;
281 if (!touched && Rect2(Point2(), bitmask->get_size()).has_point(coord)) {
282 if (bitmask->get_bitv(coord)) {
283 touched = true;
284 }
285 }
286 }
287
288 if (!touched && check_rect) {
289 if (texture_normal.is_valid()) {
290 touched = Rect2(Size2(), texture_normal->get_size()).has_point(coord);
291 }
292 }
293
294 return touched;
295}
296
297void TouchScreenButton::_press(int p_finger_pressed) {
298 finger_pressed = p_finger_pressed;
299
300 if (action != StringName()) {
301 Input::get_singleton()->action_press(action);
302 Ref<InputEventAction> iea;
303 iea.instantiate();
304 iea->set_action(action);
305 iea->set_pressed(true);
306 get_viewport()->push_input(iea, true);
307 }
308
309 emit_signal(SNAME("pressed"));
310 queue_redraw();
311}
312
313void TouchScreenButton::_release(bool p_exiting_tree) {
314 finger_pressed = -1;
315
316 if (action != StringName()) {
317 Input::get_singleton()->action_release(action);
318 if (!p_exiting_tree) {
319 Ref<InputEventAction> iea;
320 iea.instantiate();
321 iea->set_action(action);
322 iea->set_pressed(false);
323 get_viewport()->push_input(iea, true);
324 }
325 }
326
327 if (!p_exiting_tree) {
328 emit_signal(SNAME("released"));
329 queue_redraw();
330 }
331}
332
333#ifdef TOOLS_ENABLED
334Rect2 TouchScreenButton::_edit_get_rect() const {
335 if (texture_normal.is_null()) {
336 return CanvasItem::_edit_get_rect();
337 }
338
339 return Rect2(Size2(), texture_normal->get_size());
340}
341
342bool TouchScreenButton::_edit_use_rect() const {
343 return !texture_normal.is_null();
344}
345#endif
346
347Rect2 TouchScreenButton::get_anchorable_rect() const {
348 if (texture_normal.is_null()) {
349 return CanvasItem::get_anchorable_rect();
350 }
351
352 return Rect2(Size2(), texture_normal->get_size());
353}
354
355void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
356 visibility = p_mode;
357 queue_redraw();
358}
359
360TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const {
361 return visibility;
362}
363
364void TouchScreenButton::set_passby_press(bool p_enable) {
365 passby_press = p_enable;
366}
367
368bool TouchScreenButton::is_passby_press_enabled() const {
369 return passby_press;
370}
371
372#ifndef DISABLE_DEPRECATED
373bool TouchScreenButton::_set(const StringName &p_name, const Variant &p_value) {
374 if (p_name == SNAME("normal")) { // Compatibility with Godot 3.x.
375 set_texture_normal(p_value);
376 return true;
377 } else if (p_name == SNAME("pressed")) { // Compatibility with Godot 3.x.
378 set_texture_pressed(p_value);
379 return true;
380 }
381 return false;
382}
383#endif // DISABLE_DEPRECATED
384
385void TouchScreenButton::_bind_methods() {
386 ClassDB::bind_method(D_METHOD("set_texture_normal", "texture"), &TouchScreenButton::set_texture_normal);
387 ClassDB::bind_method(D_METHOD("get_texture_normal"), &TouchScreenButton::get_texture_normal);
388
389 ClassDB::bind_method(D_METHOD("set_texture_pressed", "texture"), &TouchScreenButton::set_texture_pressed);
390 ClassDB::bind_method(D_METHOD("get_texture_pressed"), &TouchScreenButton::get_texture_pressed);
391
392 ClassDB::bind_method(D_METHOD("set_bitmask", "bitmask"), &TouchScreenButton::set_bitmask);
393 ClassDB::bind_method(D_METHOD("get_bitmask"), &TouchScreenButton::get_bitmask);
394
395 ClassDB::bind_method(D_METHOD("set_shape", "shape"), &TouchScreenButton::set_shape);
396 ClassDB::bind_method(D_METHOD("get_shape"), &TouchScreenButton::get_shape);
397
398 ClassDB::bind_method(D_METHOD("set_shape_centered", "bool"), &TouchScreenButton::set_shape_centered);
399 ClassDB::bind_method(D_METHOD("is_shape_centered"), &TouchScreenButton::is_shape_centered);
400
401 ClassDB::bind_method(D_METHOD("set_shape_visible", "bool"), &TouchScreenButton::set_shape_visible);
402 ClassDB::bind_method(D_METHOD("is_shape_visible"), &TouchScreenButton::is_shape_visible);
403
404 ClassDB::bind_method(D_METHOD("set_action", "action"), &TouchScreenButton::set_action);
405 ClassDB::bind_method(D_METHOD("get_action"), &TouchScreenButton::get_action);
406
407 ClassDB::bind_method(D_METHOD("set_visibility_mode", "mode"), &TouchScreenButton::set_visibility_mode);
408 ClassDB::bind_method(D_METHOD("get_visibility_mode"), &TouchScreenButton::get_visibility_mode);
409
410 ClassDB::bind_method(D_METHOD("set_passby_press", "enabled"), &TouchScreenButton::set_passby_press);
411 ClassDB::bind_method(D_METHOD("is_passby_press_enabled"), &TouchScreenButton::is_passby_press_enabled);
412
413 ClassDB::bind_method(D_METHOD("is_pressed"), &TouchScreenButton::is_pressed);
414
415 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_normal", "get_texture_normal");
416 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_pressed", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture_pressed", "get_texture_pressed");
417 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "bitmask", PROPERTY_HINT_RESOURCE_TYPE, "BitMap"), "set_bitmask", "get_bitmask");
418 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", "get_shape");
419 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_centered"), "set_shape_centered", "is_shape_centered");
420 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shape_visible"), "set_shape_visible", "is_shape_visible");
421 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "passby_press"), "set_passby_press", "is_passby_press_enabled");
422 ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "action"), "set_action", "get_action");
423 ADD_PROPERTY(PropertyInfo(Variant::INT, "visibility_mode", PROPERTY_HINT_ENUM, "Always,TouchScreen Only"), "set_visibility_mode", "get_visibility_mode");
424
425 ADD_SIGNAL(MethodInfo("pressed"));
426 ADD_SIGNAL(MethodInfo("released"));
427
428 BIND_ENUM_CONSTANT(VISIBILITY_ALWAYS);
429 BIND_ENUM_CONSTANT(VISIBILITY_TOUCHSCREEN_ONLY);
430}
431
432TouchScreenButton::TouchScreenButton() {
433 unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D));
434 unit_rect->set_size(Vector2(1, 1));
435}
436