Bugbot/UI/ControllerInput/controller_input_select_wheel.gd

353 lines
14 KiB
GDScript

@tool
class_name ControllerInputSelectWheel
extends Control
@export var wheel_data_pack : ControllerInputWheelDataPack : set = __set_wheel_data_pack
@export_range(0.0, 2.0, 0.01) var unselected_radius : float = 1.5 : set = __set_unselected_radius
@export_range(0.0, 2.0, 0.01) var selected_radius : float = 1.5 : set = __set_selected_radius
@export var unselected_scale : Vector2 = Vector2(0.5, 0.5) : set = __set_unselected_scale
@export var selected_scale : Vector2 = Vector2(1.0, 1.0) : set = __set_selected_scale
@export_range(0.0, 1.0, 0.01) var tween_speed : float = 0.15
@export_range(0.0, 1.0, 0.01) var caret_move_delay : float = 0.25
@export_range(0.0, 1.0, 0.01) var caret_move_repeat_delay : float = 0.1
@export_group("Inputs")
@export_subgroup("Joypad", "joypad_")
@export var joypad_wheel_up : InputEventJoypadMotion
@export var joypad_wheel_down : InputEventJoypadMotion
@export var joypad_wheel_left : InputEventJoypadMotion
@export var joypad_wheel_right : InputEventJoypadMotion
@export var joypad_caret_up : InputEventJoypadButton
@export var joypad_caret_down : InputEventJoypadButton
@export var joypad_caret_left : InputEventJoypadButton
@export var joypad_caret_right : InputEventJoypadButton
@export var joypad_shift : InputEventJoypadButton
@export var joypad_backspace : InputEventJoypadButton
@export var joypad_delete : InputEventJoypadButton
@onready var caret_horizontal_move_timer : Timer = $CaretHorizontalMoveTimer
@onready var caret_vertical_move_timer : Timer = $CaretVerticalMoveTimer
@onready var caret_remove_text_timer : Timer = $CaretRemoveTextTimer
signal new_character(char:String)
signal move_caret_up
signal move_caret_down
signal move_caret_left
signal move_caret_right
signal backspace
signal delete
var __wheel_data : ControllerInputWheelData = null
var __wheels : Array = []
var __selected_wheel : int = -1
var __symbol_wheel : PackedScene = preload("res://addons/Bugbot/UI/ControllerInput/controller_input_symbol_wheel.tscn")
func _init() -> void:
resized.connect(__spin_wheels_around_centre)
func _enter_tree() -> void:
__initialise_input()
__regenerate_wheels()
func _exit_tree() -> void:
__uninitialise_input()
func _input(event:InputEvent) -> void:
if event.is_action_pressed(&"select_wheel_shift"):
__wheel_data = wheel_data_pack.uppercase
__regenerate_wheels()
elif event.is_action_released(&"select_wheel_shift"):
__wheel_data = wheel_data_pack.lowercase
__regenerate_wheels()
__move_caret(event)
__backspace_caret(event)
__delete_caret(event)
var wheels_count : int = __wheels.size()
var index : int = -1
var left_stick_vector : Vector2 = Input.get_vector(&"select_wheel_left", &"select_wheel_right", &"select_wheel_up", &"select_wheel_down", 0.95)
if left_stick_vector.length_squared() - 0.01 > 0.0:
var stick_angle : float = ((Vector2.UP).rotated(left_stick_vector.angle() + (TAU / wheels_count / 2.0)).angle() + PI) / TAU
index = int(floor(stick_angle * wheels_count))
if index != __selected_wheel:
var old_selected_wheel : int = __selected_wheel
__selected_wheel = index
if old_selected_wheel >= 0:
__deselect_wheel(old_selected_wheel)
if __selected_wheel >= 0:
__select_wheel(__selected_wheel)
func __select_wheel(selection:int) -> void:
var wheel : ControllerInputSymbolWheel = __wheels[selection]
wheel.z_index = 1
wheel.new_character.connect(__print_character)
var wheel_tween_activate : Tween = get_tree().create_tween()
wheel_tween_activate.bind_node(wheel)
wheel_tween_activate.tween_property(wheel, "scale", __calculate_wheel_scale(selection), tween_speed)
var position : Vector2 = __calculate_wheel_position(wheel, selection, selected_radius)
var wheel_tween_radius : Tween = get_tree().create_tween()
wheel_tween_radius.bind_node(wheel)
wheel_tween_radius.tween_property(wheel, "position", position, tween_speed)
wheel.select()
func __deselect_wheel(deselection:int) -> void:
var wheel : ControllerInputSymbolWheel = __wheels[deselection]
wheel.z_index = 0
wheel.new_character.disconnect(__print_character)
var wheel_tween_deactivate : Tween = get_tree().create_tween()
wheel_tween_deactivate.bind_node(wheel)
wheel_tween_deactivate.tween_property(wheel, "scale", __calculate_wheel_scale(deselection), tween_speed)
var position : Vector2 = __calculate_wheel_position(wheel, deselection, unselected_radius)
var wheel_tween_radius : Tween = get_tree().create_tween()
wheel_tween_radius.bind_node(wheel)
wheel_tween_radius.tween_property(wheel, "position", position, tween_speed)
wheel.deselect()
func __print_character(char:String) -> void:
new_character.emit(char)
func __regenerate_wheels() -> void:
for i:ControllerInputSymbolWheel in __wheels:
i.deselect()
i.queue_free()
__wheels.clear()
if not __wheel_data:
if not wheel_data_pack.lowercase: return
__wheel_data = wheel_data_pack.lowercase
for character_group:PackedStringArray in __wheel_data.characters:
var wheel : ControllerInputSymbolWheel = __symbol_wheel.instantiate()
wheel.wheel_characters = character_group
if __selected_wheel == __wheels.size():
wheel.select()
wheel.new_character.connect(__print_character)
add_child(wheel)
__wheels.append(wheel)
__spin_wheels_around_centre()
func __spin_wheels_around_centre() -> void:
if not is_inside_tree(): return
for i:int in range(__wheels.size()):
var wheel : ControllerInputSymbolWheel = __wheels[i]
var radius : float = selected_radius if __selected_wheel == i else unselected_radius
wheel.scale = __calculate_wheel_scale(i)
wheel.position = __calculate_wheel_position(wheel, i, radius)
func __calculate_wheel_position(wheel:ControllerInputSymbolWheel, wheel_index:int, radius:float) -> Vector2:
var angle_slice : float = PI / float(__wheels.size()) * 2.0
var min_size : int = min(size.x, size.y)
var max_wheel_size : int = max(wheel.size.x, wheel.size.y)
var position : Vector2 = (size / 2.0) - wheel.size / 2.0
position += (Vector2.UP * radius).rotated(angle_slice * wheel_index) * ((min_size - max_wheel_size) / 3.0)
return position
func __calculate_wheel_scale(wheel_index:int) -> Vector2:
return (selected_scale if wheel_index == __selected_wheel else unselected_scale) * __get_proportional_scale()
func __refresh_scales() -> void:
for i:int in range(__wheels.size()):
var wheel : ControllerInputSymbolWheel = __wheels[i]
wheel.scale = __calculate_wheel_scale(i)
func __initialise_input() -> void:
InputMap.add_action(&"select_wheel_up", 0.75)
InputMap.action_add_event(&"select_wheel_up", joypad_wheel_up)
InputMap.add_action(&"select_wheel_down", 0.75)
InputMap.action_add_event(&"select_wheel_down", joypad_wheel_down)
InputMap.add_action(&"select_wheel_left", 0.75)
InputMap.action_add_event(&"select_wheel_left", joypad_wheel_left)
InputMap.add_action(&"select_wheel_right", 0.75)
InputMap.action_add_event(&"select_wheel_right", joypad_wheel_right)
InputMap.add_action(&"select_wheel_caret_up", 0.75)
InputMap.action_add_event(&"select_wheel_caret_up", joypad_caret_up)
InputMap.add_action(&"select_wheel_caret_down", 0.75)
InputMap.action_add_event(&"select_wheel_caret_down", joypad_caret_down)
InputMap.add_action(&"select_wheel_caret_left", 0.75)
InputMap.action_add_event(&"select_wheel_caret_left", joypad_caret_left)
InputMap.add_action(&"select_wheel_caret_right", 0.75)
InputMap.action_add_event(&"select_wheel_caret_right", joypad_caret_right)
InputMap.add_action(&"select_wheel_shift", 0.5)
InputMap.action_add_event(&"select_wheel_shift", joypad_shift)
InputMap.add_action(&"select_wheel_backspace", 0.5)
InputMap.action_add_event(&"select_wheel_backspace", joypad_backspace)
InputMap.add_action(&"select_wheel_delete", 0.5)
InputMap.action_add_event(&"select_wheel_delete", joypad_delete)
func __uninitialise_input() -> void:
InputMap.action_erase_event(&"select_wheel_delete", joypad_delete)
InputMap.erase_action(&"select_wheel_delete")
InputMap.action_erase_event(&"select_wheel_backspace", joypad_backspace)
InputMap.erase_action(&"select_wheel_backspace")
InputMap.action_erase_event(&"select_wheel_shift", joypad_shift)
InputMap.erase_action(&"select_wheel_shift")
InputMap.action_erase_event(&"select_wheel_caret_up", joypad_caret_up)
InputMap.erase_action(&"select_wheel_caret_up")
InputMap.action_erase_event(&"select_wheel_caret_down", joypad_caret_down)
InputMap.erase_action(&"select_wheel_caret_down")
InputMap.action_erase_event(&"select_wheel_caret_left", joypad_caret_left)
InputMap.erase_action(&"select_wheel_caret_left")
InputMap.action_erase_event(&"select_wheel_caret_right", joypad_caret_right)
InputMap.erase_action(&"select_wheel_caret_right")
InputMap.action_erase_event(&"select_wheel_right", joypad_wheel_right)
InputMap.erase_action(&"select_wheel_right")
InputMap.action_erase_event(&"select_wheel_left", joypad_wheel_left)
InputMap.erase_action(&"select_wheel_left")
InputMap.action_erase_event(&"select_wheel_down", joypad_wheel_down)
InputMap.erase_action(&"select_wheel_down")
InputMap.action_erase_event(&"select_wheel_up", joypad_wheel_up)
InputMap.erase_action(&"select_wheel_up")
func __move_caret(event:InputEvent) -> void:
if event.is_action_pressed(&"select_wheel_caret_up"):
__clear_vertical_move_caret_timeouts()
move_caret_up.emit()
caret_vertical_move_timer.timeout.connect(__move_caret_up_timeout)
caret_vertical_move_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_caret_up"):
__clear_vertical_move_caret_timeouts()
if event.is_action_pressed(&"select_wheel_caret_down"):
__clear_vertical_move_caret_timeouts()
move_caret_down.emit()
caret_vertical_move_timer.timeout.connect(__move_caret_down_timeout)
caret_vertical_move_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_caret_down"):
__clear_vertical_move_caret_timeouts()
if event.is_action_pressed(&"select_wheel_caret_left"):
__clear_horizontal_move_caret_timeouts()
move_caret_left.emit()
caret_horizontal_move_timer.timeout.connect(__move_caret_left_timeout)
caret_horizontal_move_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_caret_left"):
__clear_horizontal_move_caret_timeouts()
if event.is_action_pressed(&"select_wheel_caret_right"):
__clear_horizontal_move_caret_timeouts()
move_caret_right.emit()
caret_horizontal_move_timer.timeout.connect(__move_caret_right_timeout)
caret_horizontal_move_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_caret_right"):
__clear_horizontal_move_caret_timeouts()
func __move_caret_up_timeout() -> void:
move_caret_up.emit()
caret_vertical_move_timer.start(caret_move_repeat_delay)
func __move_caret_down_timeout() -> void:
move_caret_down.emit()
caret_vertical_move_timer.start(caret_move_repeat_delay)
func __move_caret_left_timeout() -> void:
move_caret_left.emit()
caret_horizontal_move_timer.start(caret_move_repeat_delay)
func __move_caret_right_timeout() -> void:
move_caret_right.emit()
caret_horizontal_move_timer.start(caret_move_repeat_delay)
func __clear_horizontal_move_caret_timeouts() -> void:
caret_horizontal_move_timer.stop()
var connections : Array = caret_horizontal_move_timer.timeout.get_connections()
for c:Dictionary in connections:
caret_horizontal_move_timer.timeout.disconnect(c["callable"])
func __clear_vertical_move_caret_timeouts() -> void:
caret_vertical_move_timer.stop()
var connections : Array = caret_vertical_move_timer.timeout.get_connections()
for c:Dictionary in connections:
caret_vertical_move_timer.timeout.disconnect(c["callable"])
func __backspace_caret(event:InputEvent) -> void:
if event.is_action_pressed(&"select_wheel_backspace"):
__clear_backspace_caret_timeouts()
backspace.emit()
caret_remove_text_timer.timeout.connect(__backspace_caret_timeout)
caret_remove_text_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_backspace"):
__clear_backspace_caret_timeouts()
func __backspace_caret_timeout() -> void:
backspace.emit()
caret_remove_text_timer.start(caret_move_repeat_delay)
func __clear_backspace_caret_timeouts() -> void:
caret_remove_text_timer.stop()
var connections : Array = caret_remove_text_timer.timeout.get_connections()
for c:Dictionary in connections:
caret_remove_text_timer.timeout.disconnect(c["callable"])
func __delete_caret(event:InputEvent) -> void:
if event.is_action_pressed(&"select_wheel_delete"):
__clear_backspace_caret_timeouts()
delete.emit()
caret_remove_text_timer.timeout.connect(__delete_caret_timeout)
caret_remove_text_timer.start(caret_move_delay)
elif event.is_action_released(&"select_wheel_delete"):
__clear_backspace_caret_timeouts()
func __delete_caret_timeout() -> void:
delete.emit()
caret_remove_text_timer.start(caret_move_repeat_delay)
func __set_wheel_data_pack(data:ControllerInputWheelDataPack) -> void:
wheel_data_pack = data
if __wheel_data == wheel_data_pack.lowercase:
__regenerate_wheels()
func __set_unselected_radius(r:float) -> void:
unselected_radius = r
__spin_wheels_around_centre()
func __set_selected_radius(r:float) -> void:
selected_radius = r
__spin_wheels_around_centre()
func __set_unselected_scale(s:Vector2) -> void:
unselected_scale = s
__refresh_scales()
func __set_selected_scale(s:Vector2) -> void:
selected_scale = s
__refresh_scales()
func __get_proportional_scale() -> float:
var viewport_rect : Vector2 = get_viewport_rect().size
if Engine.is_editor_hint():
viewport_rect.x = ProjectSettings.get_setting("display/window/size/viewport_width")
viewport_rect.y = ProjectSettings.get_setting("display/window/size/viewport_height")
return min(size.x, size.y) / min(viewport_rect.x, viewport_rect.y)