aboutsummaryrefslogtreecommitdiff
path: root/ui/date_picker/value_picker.gd
diff options
context:
space:
mode:
authordam <dam@gudinoff>2022-04-18 09:06:19 +0000
committerdam <dam@gudinoff>2022-04-18 09:06:19 +0000
commit79691f93bab7aa093bb606bfb80d2b4b236ee091 (patch)
tree6e90c6601893895d7640f478f0cad402cc7590b4 /ui/date_picker/value_picker.gd
parent697e1ba3c4cb0a96c4584f1553de368d46287ab7 (diff)
parentee31a9a3d387121030a5f4503adeac5816d7726f (diff)
downloadsurgery-log-1.0.tar.zst
surgery-log-1.0.zip
Merge godot branch.v1.0
Diffstat (limited to 'ui/date_picker/value_picker.gd')
-rw-r--r--ui/date_picker/value_picker.gd132
1 files changed, 132 insertions, 0 deletions
diff --git a/ui/date_picker/value_picker.gd b/ui/date_picker/value_picker.gd
new file mode 100644
index 0000000..b70f1aa
--- /dev/null
+++ b/ui/date_picker/value_picker.gd
@@ -0,0 +1,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
+
+