extends Control const VELOCITY_DECAYING_FACTOR: float = 5.5 const DRAG_THRESHOLD_CM: float = 0.250 var pointer: Dictionary var anchor: float var offset: float var screen_dpcm: float onready var target: Control = get_parent() onready var target_scroll: VScrollBar = get_parent().get_v_scroll() func _ready(): pointer = { index = -1, initial_position = Vector2.ZERO, current_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): if pointer.is_active: var dragged_distance: float = - (pointer.current_position.y - pointer.initial_position.y) offset = anchor + dragged_distance self.target_scroll.value = offset elif pointer.velocity.length() > 0.5: pointer.velocity *= clamp((1.0 - VELOCITY_DECAYING_FACTOR * delta), 0.0, 1.0) offset -= pointer.velocity.y * delta self.target_scroll.value = offset 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: pointer.index = touch.index pointer.initial_position = touch.position anchor = self.target_scroll.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 = 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 simulate_gui_click(): var position := self.get_global_mouse_position() - 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 target._gui_input(event_mouse) target._gui_input(event_touch) event_mouse.pressed = false event_touch.pressed = false target._gui_input(event_mouse) target._gui_input(event_touch)