diff options
Diffstat (limited to 'date_picker/value_picker.gd')
| -rw-r--r-- | date_picker/value_picker.gd | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/date_picker/value_picker.gd b/date_picker/value_picker.gd new file mode 100644 index 0000000..98c9755 --- /dev/null +++ b/date_picker/value_picker.gd @@ -0,0 +1,126 @@ +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 + +export var min_value: int +export var max_value: int + +onready var input: LineEdit = get_node("current/input") +onready var label_previous: Label = get_node("previous") +onready var label_current: Label = get_node("current") +onready var label_next: Label = get_node("next") + +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 + + +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 + pointer.was_dragged = true + + +func input_text_entered(new_text: String): + input.release_focus() + + +func input_focus_entered(): + input.text = "%d" % value + input.visible = true + input.select_all() + + +func input_focus_exited(): + if input.text.is_valid_integer(): + value = int(input.text) + input.visible = false + + |
