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