aboutsummaryrefslogtreecommitdiff
path: root/touch_scroll.gd
blob: 7b6aefd4a0544390227b70f1869b67fe3d419e94 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
extends Control
class_name TouchScroll

const DRAG_THRESHOLD_CM: float = 0.250

export var click_target_path: String
export var scroll_target_path: String
export var scroll_bar_get_method: String
export var scroll_velocity_decaying_factor: float = 2.5

var pointer: Dictionary
var anchor: float
var offset: float
var screen_dpcm: float

var click_target: Control
var scroll_target: Control
var target_scroll_bar: VScrollBar



# @DAM
# Change this into an event-driven approach by changing the touch_scroll into a signal emitter and
# the target scrollable control a signal consumer. This way, the signal consumer will behave as the
# central node and will have the capability to detect when different touch_scroll sensors are
# activated and stop previous inputs (if required).
# Signal emitters should signal:
# - when an input drag starts
# - when an input drag stops
# - when an input drags
# - when an input clicks


func _ready():
	click_target = get_node(click_target_path)
	scroll_target = get_node(scroll_target_path)
	target_scroll_bar = scroll_target.call(scroll_bar_get_method)
	pointer = {
		index = -1,
		initial_position = Vector2.ZERO,
		current_position = Vector2.ZERO,
		relative_position = Vector2.ZERO,
		velocity = Vector2.ZERO,
		was_dragged = false,
		is_active = false,
	}
	screen_dpcm = float(OS.get_screen_dpi()) / 2.54


func _process(delta: float):
	# @DAM This only works for the stage... for the database this hides the touch_scroll after the first click.
	if click_target.has_focus():
		mouse_filter = Control.MOUSE_FILTER_IGNORE
	else:
		mouse_filter = Control.MOUSE_FILTER_STOP
	
	if pointer.is_active:
		var dragged_distance: float = - (pointer.current_position.y - pointer.initial_position.y)
		offset = anchor + dragged_distance
#		self.target_scroll_bar.value = offset
		self.target_scroll_bar.value -= pointer.relative_position.y
		pointer.relative_position = Vector2.ZERO
	elif pointer.velocity.length() > 0.5:
		pointer.velocity *= clamp((1.0 - scroll_velocity_decaying_factor * delta), 0.0, 1.0)
		offset -= pointer.velocity.y * delta
#		self.target_scroll_bar.value = offset
		self.target_scroll_bar.value -= pointer.velocity.y * delta


func _gui_input(event: InputEvent):
	
	# @DAM A bug on GODOT-3.X makes events from non-mouse-emulated pointers (index > 0) unreliable.
	if event is InputEventScreenTouch || event is InputEventScreenDrag:
		if event.index != 0:
			return
	
	if event is InputEventScreenTouch && (pointer.is_active == false || pointer.index == event.index):
		var touch := event as InputEventScreenTouch
		pointer.is_active = event.pressed
		pointer.current_position = get_global_mouse_position() # touch.position
		if pointer.is_active:
			pointer.index = touch.index
			pointer.initial_position = get_global_mouse_position() # touch.position
			anchor = self.target_scroll_bar.value
		else:
			if pointer.was_dragged == false: # Click detected.
				simulate_gui_click()
			pointer.index = -1
			pointer.was_dragged = false
	
	if event is InputEventScreenDrag && event.index == pointer.index:
		var drag := event as InputEventScreenDrag
		pointer.current_position = get_global_mouse_position() # drag.position
		pointer.velocity = drag.speed
		pointer.relative_position = drag.relative
		if pointer.current_position.distance_to(pointer.initial_position) / screen_dpcm > DRAG_THRESHOLD_CM:
			pointer.was_dragged = true


func simulate_gui_click():
	
	var position := self.get_global_mouse_position() - click_target.rect_global_position
	
	var event_touch := InputEventScreenTouch.new()
	event_touch.index = 0
	event_touch.position = position
	
	var event_mouse := InputEventMouseButton.new()
	event_mouse.button_index = BUTTON_LEFT
	event_mouse.button_mask = BUTTON_MASK_LEFT
	event_mouse.position = position
	
	event_mouse.pressed = true
	event_touch.pressed = true
	click_target._gui_input(event_mouse)
	click_target._gui_input(event_touch)
	
	click_target.grab_focus()
		
	event_mouse.pressed = false
	event_touch.pressed = false
	click_target._gui_input(event_mouse)
	click_target._gui_input(event_touch)