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