generated from krampus/template-godot4
106 lines
2.6 KiB
GDScript
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
|