106 lines
2.6 KiB
GDScript

class_name ControlBinding extends CheckerContainer
## Input for rebinding an action.
const ACTION_KEY_FMT := "ACTION_{0}"
const LISTENING_TEXT := "UI_LISTEN"
const SCENE := preload("res://src/ui/menus/settings_menu/control_binding/control_binding.tscn")
const MOTION_THRESHOLD := 0.5
@export var key: StringName
var listening: bool = false:
set(value):
if button:
button.disabled = value
listening = value
@onready var action: Label = %Action
@onready var button: Button = %Button
func _ready() -> void:
# gdlint:ignore = private-method-call
super._ready()
# Set action label text
var loc_action := tr(ACTION_KEY_FMT.format([key]))
# Fall back to just the key if no localization exists
@warning_ignore("incompatible_ternary")
action.text = loc_action if loc_action else key
# Set the binding label
_set_label_from_binding()
func _set_label_from_binding() -> void:
var actions := InputMap.action_get_events(key)
if actions:
var primary := actions[0]
button.text = PromptMap.from_event(primary)
if button.text == PromptMap.UNKNOWN_INPUT_SYMBOL:
print_debug("No mapping for input event: ", primary)
# Special case: Can't rebind things bound to ESC
if primary is InputEventKey and (primary as InputEventKey).physical_keycode == KEY_ESCAPE:
button.disabled = true
func _input(event: InputEvent) -> void:
if not listening:
return
if event is InputEventKey:
var key_event: InputEventKey = event
if (
key_event.physical_keycode in [KEY_CTRL, KEY_ALT, KEY_SHIFT, KEY_META]
and key_event.pressed
):
# Ignore modifier key until release
return
if key_event.physical_keycode == KEY_ESCAPE:
get_viewport().set_input_as_handled()
cancel_rebinding()
return
if event is InputEventJoypadMotion:
var motion_event: InputEventJoypadMotion = event
if abs(motion_event.axis_value) < MOTION_THRESHOLD:
# Ignore axis motion unless it's over our threshold
return
if (
event is InputEventKey
or event is InputEventMouseButton
or event is InputEventJoypadButton
or event is InputEventJoypadMotion
):
get_viewport().set_input_as_handled()
rebind(event)
func start_listening() -> void:
button.text = LISTENING_TEXT
listening = true
func cancel_rebinding() -> void:
_set_label_from_binding()
listening = false
func rebind(event: InputEvent) -> void:
# Clear previous binding
InputMap.action_erase_events(key)
# Add new binding
InputMap.action_add_event(key, event)
# Update label
_set_label_from_binding()
listening = false
static func create(_key: StringName) -> ControlBinding:
var instance: ControlBinding = SCENE.instantiate()
instance.key = _key
return instance