aboutsummaryrefslogtreecommitdiff
path: root/game/addons/keychain/Keychain.gd
diff options
context:
space:
mode:
Diffstat (limited to 'game/addons/keychain/Keychain.gd')
-rw-r--r--game/addons/keychain/Keychain.gd174
1 files changed, 45 insertions, 129 deletions
diff --git a/game/addons/keychain/Keychain.gd b/game/addons/keychain/Keychain.gd
index ff939f3..2288107 100644
--- a/game/addons/keychain/Keychain.gd
+++ b/game/addons/keychain/Keychain.gd
@@ -1,22 +1,29 @@
extends Node
-const TRANSLATIONS_PATH := "res://addons/keychain/translations"
const PROFILES_PATH := "user://shortcut_profiles"
-# Change these settings
-var profiles := [preload("profiles/default.tres")]
-var selected_profile: ShortcutProfile = profiles[0]
-var profile_index := 0
-# Syntax: "action_name": InputAction.new("Action Display Name", "Group", true)
-# Note that "action_name" must already exist in the Project's Input Map.
+## [Array] of [ShortcutProfile]s.
+var profiles: Array[ShortcutProfile] = [preload("profiles/default.tres")]
+var selected_profile := profiles[0] ## The currently selected [ShortcutProfile].
+var profile_index := 0 ## The index of the currently selected [ShortcutProfile].
+## [Dictionary] of [String] and [InputAction].
+## Syntax: "action_name": InputAction.new("Action Display Name", "Group", true)
+## Note that "action_name" must already exist in the Project's Input Map.
var actions := {}
-# Syntax: "Group Name": InputGroup.new("Parent Group Name")
+## [Dictionary] of [String] and [InputGroup].
+## Syntax: "Group Name": InputGroup.new("Parent Group Name")
var groups := {}
-var ignore_actions := []
+var ignore_actions: Array[StringName] = [] ## [Array] of [StringName] input map actions to ignore.
+## If [code]true[/code], ignore Godot's default "ui_" input map actions.
var ignore_ui_actions := true
-var changeable_types := [true, true, true, true]
-var multiple_menu_accelerators := false
+## A [PackedByteArray] of [bool]s with a fixed length of 4. Used for developers to allow or
+## forbid setting certain types of InputEvents. The first element is for [InputEventKey]s,
+## the second for [InputEventMouseButton]s, the third for [InputEventJoypadButton]s
+## and the fourth for [InputEventJoypadMotion]s.
+var changeable_types: PackedByteArray = [true, true, true, true]
+## The file path of the [code]config_file[/code].
var config_path := "user://cache.ini"
+## Used to store the settings to the filesystem.
var config_file: ConfigFile
@@ -25,79 +32,11 @@ class InputAction:
var group := ""
var global := true
- func _init(_display_name := "",_group := "",_global := true):
+ func _init(_display_name := "", _group := "", _global := true):
display_name = _display_name
group = _group
global = _global
- func update_node(_action: String) -> void:
- pass
-
- func handle_input(_event: InputEvent, _action: String) -> bool:
- return false
-
-
-# This class is useful for the accelerators of PopupMenu items
-# It's possible for PopupMenu items to have multiple shortcuts by using
-# set_item_shortcut(), but we have no control over the accelerator text that appears.
-# Thus, we are stuck with using accelerators instead of shortcuts.
-# If Godot ever receives the ability to change the accelerator text of the items,
-# we could in theory remove this class.
-# If you don't care about PopupMenus in the same scene as ShortcutEdit
-# such as projects like Pixelorama where everything is in the same scene,
-# then you can ignore this class.
-class MenuInputAction:
- extends InputAction
- var node_path := ""
- var node: PopupMenu
- var menu_item_id := 0
- var echo := false
-
- func _init(
- _display_name := "",
- _group := "",
- _global := true,
- _node_path := "",
- _menu_item_id := 0,
- _echo := false
- ) -> void:
- super._init(_display_name, _group, _global)
- node_path = _node_path
- menu_item_id = _menu_item_id
- echo = _echo
-
- func get_node(root: Node) -> void:
- var temp_node = root.get_node(node_path)
- if temp_node is PopupMenu:
- node = node
- elif temp_node is MenuButton:
- node = temp_node.get_popup()
-
- func update_node(action: String) -> void:
- if !node:
- return
- var first_key: InputEventKey = Keychain.action_get_first_key(action)
- var accel := first_key.get_keycode_with_modifiers() if first_key else 0
- node.set_item_accelerator(menu_item_id, accel)
-
- func handle_input(event: InputEvent, action: String) -> bool:
- if not node:
- return false
- if event.is_action_pressed(action, false, true):
- if event is InputEventKey:
- var acc: int = node.get_item_accelerator(menu_item_id)
- # If the event is the same as the menu item's accelerator, skip
- if acc == event.get_keycode_with_modifiers():
- return true
- node.emit_signal("id_pressed", menu_item_id)
- return true
- if event.is_action(action, true) and echo:
- if event.is_echo():
- node.emit_signal("id_pressed", menu_item_id)
- return true
-
- return false
-
class InputGroup:
var parent_group := ""
@@ -115,13 +54,10 @@ func _ready() -> void:
if !config_path.is_empty():
config_file.load(config_path)
- set_process_input(multiple_menu_accelerators)
-
# Load shortcut profiles
DirAccess.make_dir_recursive_absolute(PROFILES_PATH)
var profile_dir := DirAccess.open(PROFILES_PATH)
- profile_dir.make_dir(PROFILES_PATH)
- profile_dir.list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
+ profile_dir.list_dir_begin()
var file_name = profile_dir.get_next()
while file_name != "":
if !profile_dir.current_is_dir():
@@ -143,35 +79,9 @@ func _ready() -> void:
for profile in profiles:
profile.fill_bindings()
- var l18n_dir := DirAccess.open(TRANSLATIONS_PATH)
- l18n_dir.list_dir_begin() # TODOGODOT4 fill missing arguments https://github.com/godotengine/godot/pull/40547
- file_name = l18n_dir.get_next()
- while file_name != "":
- if !l18n_dir.current_is_dir():
- if file_name.get_extension() == "po":
- var t: Translation = load(TRANSLATIONS_PATH.path_join(file_name))
- TranslationServer.add_translation(t)
- file_name = l18n_dir.get_next()
-
profile_index = config_file.get_value("shortcuts", "shortcuts_profile", 0)
change_profile(profile_index)
- for action in actions:
- var input_action: InputAction = actions[action]
- if input_action is MenuInputAction:
- input_action.get_node(get_tree().current_scene)
-
-
-func _input(event: InputEvent) -> void:
- if event is InputEventMouseMotion:
- return
-
- for action in actions:
- var input_action: InputAction = actions[action]
- var done: bool = input_action.handle_input(event, action)
- if done:
- return
-
func change_profile(index: int) -> void:
if index >= profiles.size():
@@ -184,29 +94,35 @@ func change_profile(index: int) -> void:
action_add_event(action, event)
-func action_add_event(action: String, event: InputEvent) -> void:
+func action_add_event(action: StringName, event: InputEvent) -> void:
InputMap.action_add_event(action, event)
- if action in actions:
- actions[action].update_node(action)
-func action_erase_event(action: String, event: InputEvent) -> void:
+func action_erase_event(action: StringName, event: InputEvent) -> void:
InputMap.action_erase_event(action, event)
- if action in actions:
- actions[action].update_node(action)
-func action_erase_events(action: String) -> void:
+func action_erase_events(action: StringName) -> void:
InputMap.action_erase_events(action)
- if action in actions:
- actions[action].update_node(action)
-
-
-func action_get_first_key(action: String) -> InputEventKey:
- var first_key: InputEventKey = null
- var events := InputMap.action_get_events(action)
- for event in events:
- if event is InputEventKey:
- first_key = event
- break
- return first_key
+
+
+func load_translation(locale: String) -> void:
+ var translation = load("res://addons/keychain/translations".path_join(locale + ".po"))
+ if is_instance_valid(translation) and translation is Translation:
+ TranslationServer.add_translation(translation)
+
+
+## Converts a [param text] with snake case to a more readable format, by replacing
+## underscores with spaces. If [param capitalize_first_letter] is [code]true[/code],
+## the first letter of the text is capitalized.
+## E.g, "snake_case" would be converted to "Snake case" if
+## [param capitalize_first_letter] is [code]true[/code], else it would be converted to
+## "snake case".
+func humanize_snake_case(text: String, capitalize_first_letter := true) -> String:
+ text = text.replace("_", " ")
+ if capitalize_first_letter:
+ var first_letter := text.left(1)
+ first_letter = first_letter.capitalize()
+ text = text.right(-1)
+ text = text.insert(0, first_letter)
+ return text