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
126
127
128
129
130
131
132
|
extends Control
class_name ValuePicker
const VELOCITY_DECAYING_FACTOR: float = 5.5
const BINNING_THRESHOLD: float = 0.75
const BINNING_ADJUST_P: float = 5.0
const DRAG_THRESHOLD_CM: float = 0.250
export var min_value: int
export var max_value: int
var pointer: Dictionary
var anchor: float
var value: int
var offset: float
var scroll_unit_height: float
var label_previous_base_position: float
var label_current_base_position: float
var label_next_base_position: float
onready var input := get_node("current/input") as LineEdit
onready var label_previous := get_node("previous") as Label
onready var label_current := get_node("current") as Label
onready var label_next := get_node("next") as Label
onready var screen_dpcm := float(OS.get_screen_dpi()) / 2.54
func _ready():
pointer = {
index = -1,
initial_position = Vector2.ZERO,
current_position = Vector2.ZERO,
velocity = Vector2.ZERO,
was_dragged = false,
is_active = false,
}
input.connect("text_entered", self, "input_text_entered")
input.connect("focus_entered", self, "input_focus_entered")
input.connect("focus_exited", self, "input_focus_exited")
scroll_unit_height = label_current.rect_size.y
label_previous_base_position = label_previous.rect_position.y
label_current_base_position = label_current.rect_position.y
label_next_base_position = label_next.rect_position.y
value = min_value
offset = 0.0
func _process(delta: float):
if pointer.is_active:
var dragged_distance: float = - (pointer.current_position.y - pointer.initial_position.y)
offset = anchor + (dragged_distance / scroll_unit_height)
value = int(offset)
offset -= value
else:
pointer.velocity *= clamp((1.0 - VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0)
offset -= pointer.velocity.y * delta / scroll_unit_height
if abs(pointer.velocity.y) < BINNING_THRESHOLD * scroll_unit_height:
offset -= offset * BINNING_ADJUST_P * delta
# Using 'offset * 2.0' (equivalent to 'offset / 0.5') rounds the value based on 0.5 units.
var cummulative_displacement := int(offset * 2.0)
value = wrapi(value + cummulative_displacement, min_value, max_value + 1)
offset -= cummulative_displacement
label_current.text = "%d" % value
label_next.text = "%d" % wrapi(value + 1, min_value, max_value + 1)
label_previous.text = "%d" % wrapi(value - 1, min_value, max_value + 1)
var offset_position := offset * scroll_unit_height
label_current.rect_position.y = label_current_base_position - offset_position
label_previous.rect_position.y = label_previous_base_position - offset_position
label_next.rect_position.y = label_next_base_position - offset_position
label_previous.modulate.a = 0.5 - offset
label_next.modulate.a = offset + 0.5
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 = touch.position
if pointer.is_active:
input.release_focus()
pointer.index = touch.index
pointer.initial_position = touch.position
anchor = value + offset
else:
if pointer.was_dragged == false: # Click detected.
input.grab_focus()
pointer.index = -1
pointer.was_dragged = false
if event is InputEventScreenDrag && event.index == pointer.index:
var drag := event as InputEventScreenDrag
pointer.current_position = drag.position
pointer.velocity = drag.speed
if pointer.current_position.distance_to(pointer.initial_position) / screen_dpcm > DRAG_THRESHOLD_CM:
pointer.was_dragged = true
func input_text_entered(new_text: String):
input.release_focus()
func input_focus_entered():
pointer.velocity = Vector2.ZERO # Avoid changing to other value once entering input.
input.text = "%d" % value
input.visible = true
input.select_all()
label_current.self_modulate.a = 0.0
func input_focus_exited():
if input.text.is_valid_integer():
value = wrapi(int(input.text), min_value, max_value + 1)
input.visible = false
label_current.self_modulate.a = 1.0
|