149 lines
3.8 KiB
GDScript
149 lines
3.8 KiB
GDScript
extends ScrollContainer
|
|
|
|
signal page_changed(index: int)
|
|
|
|
const SNAP_DURATION := 0.25
|
|
const TEX_DEFAULT := preload("res://assets/graphics/gui/lobby/bar_default.svg")
|
|
const TEX_ACTIVE := preload("res://assets/graphics/gui/lobby/bar_active.svg")
|
|
|
|
@export var auto_advance_enabled: bool = true
|
|
@export var auto_advance_delay: float = 4.0
|
|
|
|
@onready var _track: HBoxContainer = $CardMargin/HBoxContainer
|
|
@onready var _page_dots: Array = get_node_or_null("../PageIndicator").get_children() if get_node_or_null("../PageIndicator") else []
|
|
|
|
var _dragging := false
|
|
var _drag_start_pos := 0.0
|
|
var _drag_start_scroll := 0
|
|
var _snap_tween: Tween
|
|
var _auto_timer: Timer
|
|
var _last_reported_page := -1
|
|
|
|
|
|
func _ready() -> void:
|
|
_update_indicator()
|
|
_setup_auto_timer()
|
|
|
|
|
|
func _setup_auto_timer() -> void:
|
|
_auto_timer = Timer.new()
|
|
_auto_timer.name = "AutoAdvanceTimer"
|
|
_auto_timer.one_shot = true
|
|
_auto_timer.timeout.connect(_on_auto_timer_timeout)
|
|
add_child(_auto_timer)
|
|
_restart_auto_timer()
|
|
|
|
|
|
func _restart_auto_timer() -> void:
|
|
if not auto_advance_enabled or _dragging or _card_count() <= 1:
|
|
return
|
|
_auto_timer.stop()
|
|
_auto_timer.wait_time = auto_advance_delay
|
|
_auto_timer.start()
|
|
|
|
|
|
func _on_auto_timer_timeout() -> void:
|
|
if _dragging:
|
|
return
|
|
var next_index := (_current_page() + 1) % _card_count()
|
|
_animate_to_page(next_index)
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
if _auto_timer:
|
|
_auto_timer.stop()
|
|
if _snap_tween:
|
|
_snap_tween.kill()
|
|
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
|
|
if event.pressed:
|
|
_start_drag(event.position.x)
|
|
elif _dragging:
|
|
_end_drag()
|
|
elif event is InputEventMouseMotion and _dragging:
|
|
_update_drag(event.position.x)
|
|
elif event is InputEventScreenTouch and event.index == 0:
|
|
if event.pressed:
|
|
_start_drag(event.position.x)
|
|
elif _dragging:
|
|
_end_drag()
|
|
elif event is InputEventScreenDrag and event.index == 0 and _dragging:
|
|
_update_drag(event.position.x)
|
|
|
|
|
|
func _start_drag(x: float) -> void:
|
|
if _snap_tween:
|
|
_snap_tween.kill()
|
|
_auto_timer.stop()
|
|
_dragging = true
|
|
_drag_start_pos = x
|
|
_drag_start_scroll = scroll_horizontal
|
|
|
|
|
|
func _update_drag(x: float) -> void:
|
|
var delta := _drag_start_pos - x
|
|
scroll_horizontal = clampi(_drag_start_scroll + int(delta), 0, _max_scroll())
|
|
_update_indicator()
|
|
|
|
|
|
func _end_drag() -> void:
|
|
_dragging = false
|
|
_snap_to_nearest_card()
|
|
|
|
|
|
func _max_scroll() -> int:
|
|
var bar := get_h_scroll_bar()
|
|
return maxi(0, int(bar.max_value - bar.page))
|
|
|
|
|
|
func _card_count() -> int:
|
|
return maxi(1, _track.get_child_count())
|
|
|
|
|
|
func _card_step() -> float:
|
|
var count := _card_count()
|
|
if count <= 1:
|
|
return 0.0
|
|
return float(_max_scroll()) / float(count - 1)
|
|
|
|
|
|
func _current_page() -> int:
|
|
var step := _card_step()
|
|
if step <= 0.0:
|
|
return 0
|
|
return clampi(roundi(scroll_horizontal / step), 0, _card_count() - 1)
|
|
|
|
|
|
func _snap_to_nearest_card() -> void:
|
|
_animate_to_page(_current_page())
|
|
|
|
|
|
func _animate_to_page(target_index: int) -> void:
|
|
target_index = clampi(target_index, 0, _card_count() - 1)
|
|
var step := _card_step()
|
|
var target_scroll := int(round(target_index * step))
|
|
if _snap_tween:
|
|
_snap_tween.kill()
|
|
_snap_tween = create_tween().set_trans(Tween.TRANS_CUBIC).set_ease(Tween.EASE_OUT)
|
|
_snap_tween.tween_method(_set_scroll_and_indicator, float(scroll_horizontal), float(target_scroll), SNAP_DURATION)
|
|
_snap_tween.finished.connect(_restart_auto_timer)
|
|
|
|
|
|
func _set_scroll_and_indicator(value: float) -> void:
|
|
scroll_horizontal = int(round(value))
|
|
_update_indicator()
|
|
|
|
|
|
func _update_indicator() -> void:
|
|
if _page_dots.is_empty():
|
|
return
|
|
var active_index := _current_page()
|
|
for i in _page_dots.size():
|
|
var dot: TextureRect = _page_dots[i]
|
|
dot.texture = TEX_ACTIVE if i == active_index else TEX_DEFAULT
|
|
if active_index != _last_reported_page:
|
|
_last_reported_page = active_index
|
|
page_changed.emit(active_index)
|