1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
extends ConfirmationDialog
enum InputTypes { KEYBOARD, MOUSE, JOY_BUTTON, JOY_AXIS }
@export var input_type: InputTypes = InputTypes.KEYBOARD
var listened_input: InputEvent
@onready var root: Node = get_parent()
@onready var input_type_l: Label = $VBoxContainer/InputTypeLabel
@onready var entered_shortcut: LineEdit = $VBoxContainer/EnteredShortcut
@onready var option_button: OptionButton = $VBoxContainer/OptionButton
@onready var already_exists: Label = $VBoxContainer/AlreadyExistsLabel
func _ready() -> void:
set_process_input(false)
if input_type == InputTypes.KEYBOARD:
get_ok_button().focus_neighbor_top = entered_shortcut.get_path()
get_cancel_button().focus_neighbor_top = entered_shortcut.get_path()
entered_shortcut.focus_neighbor_bottom = get_ok_button().get_path()
else:
get_ok_button().focus_neighbor_top = option_button.get_path()
get_cancel_button().focus_neighbor_top = option_button.get_path()
option_button.focus_neighbor_bottom = get_ok_button().get_path()
# get_close_button().focus_mode = Control.FOCUS_NONE
func _input(event: InputEvent) -> void:
if not event is InputEventKey:
return
if event.pressed:
listened_input = event
entered_shortcut.text = OS.get_keycode_string(event.get_keycode_with_modifiers())
_show_assigned_state(event)
func _show_assigned_state(event: InputEvent) -> void:
var metadata = root.currently_editing_tree_item.get_metadata(0)
var action := ""
if metadata is InputEvent: # Editing an input event
action = root.currently_editing_tree_item.get_parent().get_metadata(0)
elif metadata is StringName: # Adding a new input event to an action
action = metadata
var matching_pair: Array = _find_matching_event_in_map(action, event)
if matching_pair:
already_exists.text = tr("Already assigned to: %s") % root.get_action_name(matching_pair[0])
else:
already_exists.text = ""
func _on_ShortcutSelectorDialog_confirmed() -> void:
if listened_input == null:
return
_apply_shortcut_change(listened_input)
func _apply_shortcut_change(input_event: InputEvent) -> void:
var metadata = root.currently_editing_tree_item.get_metadata(0)
if metadata is InputEvent: # Editing an input event
var parent_metadata = root.currently_editing_tree_item.get_parent().get_metadata(0)
var changed: bool = _set_shortcut(parent_metadata, metadata, input_event)
if !changed:
return
root.currently_editing_tree_item.set_metadata(0, input_event)
root.currently_editing_tree_item.set_text(0, root.event_to_str(input_event))
elif metadata is StringName: # Adding a new input event to an action
var changed: bool = _set_shortcut(metadata, null, input_event)
if !changed:
return
root.add_event_tree_item(input_event, root.currently_editing_tree_item)
func _set_shortcut(action: StringName, old_event: InputEvent, new_event: InputEvent) -> bool:
if InputMap.action_has_event(action, new_event): # If the current action already has that event
return false
if old_event:
Keychain.action_erase_event(action, old_event)
# Loop through other actions to see if the event exists there, to re-assign it
var matching_pair := _find_matching_event_in_map(action, new_event)
if matching_pair:
var group := ""
if action in Keychain.actions:
group = Keychain.actions[action].group
var action_to_replace: StringName = matching_pair[0]
var input_to_replace: InputEvent = matching_pair[1]
Keychain.action_erase_event(action_to_replace, input_to_replace)
Keychain.selected_profile.change_action(action_to_replace)
var tree_item: TreeItem = root.tree.get_root()
var prev_tree_item: TreeItem
while tree_item != null: # Loop through Tree's TreeItems...
var metadata = tree_item.get_metadata(0)
if metadata is InputEvent:
if input_to_replace.is_match(metadata):
var map_action: StringName = tree_item.get_parent().get_metadata(0)
if map_action == action_to_replace:
tree_item.free()
break
tree_item = _get_next_tree_item(tree_item)
Keychain.action_add_event(action, new_event)
Keychain.selected_profile.change_action(action)
return true
# Based on https://github.com/godotengine/godot/blob/master/scene/gui/tree.cpp#L685
func _get_next_tree_item(current: TreeItem) -> TreeItem:
if current.get_first_child():
current = current.get_first_child()
elif current.get_next():
current = current.get_next()
else:
while current and !current.get_next():
current = current.get_parent()
if current:
current = current.get_next()
return current
func _find_matching_event_in_map(action: StringName, event: InputEvent) -> Array:
var group := ""
if action in Keychain.actions:
group = Keychain.actions[action].group
for map_action in InputMap.get_actions():
if map_action in Keychain.ignore_actions:
continue
if Keychain.ignore_ui_actions and (map_action as String).begins_with("ui_"):
continue
for map_event in InputMap.action_get_events(map_action):
if !event.is_match(map_event):
continue
if map_action in Keychain.actions:
# If it's local, check if it's the same group, otherwise ignore
if !Keychain.actions[action].global or !Keychain.actions[map_action].global:
if Keychain.actions[map_action].group != group:
continue
return [map_action, map_event]
return []
func _on_ShortcutSelectorDialog_about_to_show() -> void:
if input_type == InputTypes.KEYBOARD:
listened_input = null
already_exists.text = ""
entered_shortcut.text = ""
await get_tree().process_frame
entered_shortcut.grab_focus()
else:
var metadata = root.currently_editing_tree_item.get_metadata(0)
if metadata is InputEvent: # Editing an input event
var index := 0
if metadata is InputEventMouseButton:
index = metadata.button_index - 1
elif metadata is InputEventJoypadButton:
index = metadata.button_index
elif metadata is InputEventJoypadMotion:
index = metadata.axis * 2
index += round(metadata.axis_value) / 2.0 + 0.5
option_button.select(index)
_on_OptionButton_item_selected(index)
elif metadata is StringName: # Adding a new input event to an action
option_button.select(0)
_on_OptionButton_item_selected(0)
func _on_ShortcutSelectorDialog_popup_hide() -> void:
set_process_input(false)
func _on_OptionButton_item_selected(index: int) -> void:
if input_type == InputTypes.MOUSE:
listened_input = InputEventMouseButton.new()
listened_input.button_index = index + 1
elif input_type == InputTypes.JOY_BUTTON:
listened_input = InputEventJoypadButton.new()
listened_input.button_index = index
elif input_type == InputTypes.JOY_AXIS:
listened_input = InputEventJoypadMotion.new()
listened_input.axis = index / 2
listened_input.axis_value = -1.0 if index % 2 == 0 else 1.0
_show_assigned_state(listened_input)
func _on_EnteredShortcut_focus_entered() -> void:
set_process_input(true)
func _on_EnteredShortcut_focus_exited() -> void:
set_process_input(false)
|