diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index 0dd17678..451b0f1a 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -1,14 +1,23 @@ #include "UITools.hpp" + #include +#include #include +#include +#include +#include +#include #include #include #include +#include #include #include #include +#include #include +#include #include "openvic-extension/classes/GUIButton.hpp" #include "openvic-extension/classes/GUIIcon.hpp" @@ -24,10 +33,6 @@ #include "openvic-extension/singletons/AssetManager.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/utility/Utilities.hpp" -#include "godot_cpp/classes/global_constants.hpp" -#include "godot_cpp/classes/input_event_key.hpp" -#include "godot_cpp/classes/shortcut.hpp" -#include "godot_cpp/core/error_macros.hpp" using namespace godot; using namespace OpenVic; @@ -68,7 +73,7 @@ GUI::Position const* UITools::get_gui_position(String const& gui_scene, String c static Array get_events_from_shortcut_key(String const& key) { Array events; - if (key.length() == 0) { + if (key.is_empty()) { return events; } @@ -123,6 +128,69 @@ static Array get_events_from_shortcut_key(String const& key) { return events; } +static Error try_create_shortcut_action_for_button( + GUIButton* gui_button, String const& shortcut_key_name, String const& shortcut_hotkey_name = "" +) { + Array event_array = get_events_from_shortcut_key(shortcut_key_name); + + ERR_FAIL_COND_V_MSG( + shortcut_key_name.length() != 0 && event_array.size() == 0, ERR_INVALID_PARAMETER, + vformat("Unknown shortcut key '%s' for GUI button %s", shortcut_key_name, gui_button->get_name()) + ); + + if (shortcut_key_name.is_empty()) { + return OK; + } + + InputMap* const im = InputMap::get_singleton(); + String action_name; + if (shortcut_hotkey_name.is_empty()) { + action_name = // + vformat("button_%s_hotkey", gui_button->get_name().to_lower().replace("button", "").replace("hotkey", "")) + .replace("__", "_"); + } else { + action_name = vformat("button_%s_hotkey", shortcut_hotkey_name); + } + Ref action_event; + action_event.instantiate(); + action_event->set_action(action_name); + action_event->set_pressed(true); + + if (im->has_action(action_name)) { + TypedArray events = im->action_get_events(action_name); + bool skip_warning = events.size() != event_array.size(); + if (!skip_warning) { + for (std::size_t index = 0; index < events.size(); index++) { + if (!event_array.has(events[index])) { + skip_warning = false; + break; + } + } + } + + if (!skip_warning) { + WARN_PRINT(vformat("'%s' already found in InputMap with different values, reusing hotkey", action_name)); + } + } else { + im->add_action(action_name); + for (std::size_t index = 0; index < event_array.size(); index++) { + Ref event = event_array[index]; + ERR_CONTINUE(event.is_null()); + im->action_add_event(action_name, event); + } + } + + Array shortcut_array; + shortcut_array.push_back(action_event); + + Ref shortcut; + shortcut.instantiate(); + shortcut->set_events(shortcut_array); + gui_button->set_shortcut(shortcut); + + return OK; +} + /* GUI::Element tree -> godot::Control tree conversion code below: */ namespace OpenVic { @@ -283,12 +351,7 @@ static bool generate_button(generate_gui_args_t&& args) { // TODO - clicksound, rotation (?) const String button_name = Utilities::std_to_godot_string(button.get_name()); const String shortcut_key_name = Utilities::std_to_godot_string(button.get_shortcut()); - Array event_array = get_events_from_shortcut_key(shortcut_key_name); - ERR_FAIL_COND_V_MSG( - shortcut_key_name.length() != 0 && event_array.size() == 0, false, - vformat("Unknown shortcut key '%s' for GUI button %s", shortcut_key_name, button_name) - ); ERR_FAIL_NULL_V_MSG(button.get_sprite(), false, vformat("Null sprite for GUI button %s", button_name)); GUIButton* gui_button = nullptr; @@ -335,11 +398,8 @@ static bool generate_button(generate_gui_args_t&& args) { ret &= gui_button->set_gfx_font(button.get_font()) == OK; } - if (shortcut_key_name.length() != 0) { - Ref shortcut; - shortcut.instantiate(); - shortcut->set_events(event_array); - gui_button->set_shortcut(shortcut); + if (try_create_shortcut_action_for_button(gui_button, shortcut_key_name) != OK) { + WARN_PRINT(vformat("Failed to create shortcut for GUI button '%s'", button_name)); } gui_button->set_shortcut_feedback(false); @@ -354,12 +414,7 @@ static bool generate_checkbox(generate_gui_args_t&& args) { const String checkbox_name = Utilities::std_to_godot_string(checkbox.get_name()); const String shortcut_key_name = Utilities::std_to_godot_string(checkbox.get_shortcut()); - Array event_array = get_events_from_shortcut_key(shortcut_key_name); - ERR_FAIL_COND_V_MSG( - shortcut_key_name.length() != 0 && event_array.size() == 0, false, - vformat("Unknown shortcut key '%s' for GUI checkbox %s", shortcut_key_name, checkbox_name) - ); ERR_FAIL_NULL_V_MSG(checkbox.get_sprite(), false, vformat("Null sprite for GUI checkbox %s", checkbox_name)); GFX::IconTextureSprite const* texture_sprite = checkbox.get_sprite()->cast_to(); @@ -390,11 +445,8 @@ static bool generate_checkbox(generate_gui_args_t&& args) { ret &= gui_icon_button->set_gfx_font(checkbox.get_font()) == OK; } - if (shortcut_key_name.length() != 0) { - Ref shortcut; - shortcut.instantiate(); - shortcut->set_events(event_array); - gui_icon_button->set_shortcut(shortcut); + if (try_create_shortcut_action_for_button(gui_icon_button, shortcut_key_name) != OK) { + WARN_PRINT(vformat("Failed to create shortcut hotkey for GUI checkbox '%s'", checkbox_name)); } gui_icon_button->set_shortcut_feedback(false); diff --git a/game/addons/keychain/Keychain.gd b/game/addons/keychain/Keychain.gd index 2288107b..f99afb98 100644 --- a/game/addons/keychain/Keychain.gd +++ b/game/addons/keychain/Keychain.gd @@ -2,6 +2,8 @@ extends Node const PROFILES_PATH := "user://shortcut_profiles" +signal reload_keychain() + ## [Array] of [ShortcutProfile]s. var profiles: Array[ShortcutProfile] = [preload("profiles/default.tres")] var selected_profile := profiles[0] ## The currently selected [ShortcutProfile]. @@ -22,9 +24,12 @@ var ignore_ui_actions := true ## 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" +var config_path := "user://config.ini" ## Used to store the settings to the filesystem. var config_file: ConfigFile +## Used to check if unused binding check should be ignored for action +var keep_binding_check : Callable = func(action_name : StringName) -> bool: + return action_name.begins_with("button_") and action_name.ends_with("_hotkey") class InputAction: @@ -48,6 +53,10 @@ class InputGroup: folded = _folded +func _init() -> void: + for locale in TranslationServer.get_loaded_locales(): + load_translation(locale) + func _ready() -> void: if !config_file: config_file = ConfigFile.new() @@ -58,11 +67,11 @@ func _ready() -> void: DirAccess.make_dir_recursive_absolute(PROFILES_PATH) var profile_dir := DirAccess.open(PROFILES_PATH) profile_dir.list_dir_begin() - var file_name = profile_dir.get_next() + var file_name := profile_dir.get_next() while file_name != "": if !profile_dir.current_is_dir(): if file_name.get_extension() == "tres": - var file = load(PROFILES_PATH.path_join(file_name)) + var file := load(PROFILES_PATH.path_join(file_name)) if file is ShortcutProfile: profiles.append(file) file_name = profile_dir.get_next() @@ -76,12 +85,15 @@ func _ready() -> void: if saved: profiles.append(profile) + initialize_profiles() + +func initialize_profiles() -> void: for profile in profiles: profile.fill_bindings() profile_index = config_file.get_value("shortcuts", "shortcuts_profile", 0) change_profile(profile_index) - + Keychain.reload_keychain.emit() func change_profile(index: int) -> void: if index >= profiles.size(): @@ -89,6 +101,7 @@ func change_profile(index: int) -> void: profile_index = index selected_profile = profiles[index] for action in selected_profile.bindings: + if not InputMap.has_action(action): continue action_erase_events(action) for event in selected_profile.bindings[action]: action_add_event(action, event) @@ -107,7 +120,10 @@ func action_erase_events(action: StringName) -> void: func load_translation(locale: String) -> void: - var translation = load("res://addons/keychain/translations".path_join(locale + ".po")) + var translation_file_path := "res://addons/keychain/translations".path_join(locale + ".po") + if not ResourceLoader.exists(translation_file_path, "Translation"): + return + var translation := load(translation_file_path) if is_instance_valid(translation) and translation is Translation: TranslationServer.add_translation(translation) diff --git a/game/addons/keychain/ShortcutEdit.gd b/game/addons/keychain/ShortcutEdit.gd index 207ddec1..40a4ee49 100644 --- a/game/addons/keychain/ShortcutEdit.gd +++ b/game/addons/keychain/ShortcutEdit.gd @@ -10,8 +10,8 @@ const MOUSE_BUTTON_NAMES: PackedStringArray = [ "Wheel Down Button", "Wheel Left Button", "Wheel Right Button", - "X Button 1", - "X Button 2", + "Mouse Thumb Button 1", + "Mouse Thumb Button 2", ] const JOY_BUTTON_NAMES: PackedStringArray = [ @@ -110,6 +110,10 @@ func _ready() -> void: if OS.get_name() == "Web": $VBoxContainer/HBoxContainer/OpenProfileFolder.queue_free() + Keychain.reload_keychain.connect(_on_reload_keychain) + +func _on_reload_keychain() -> void: + _on_ProfileOptionButton_item_selected(Keychain.profile_index) func _construct_tree() -> void: var buttons_disabled := false if Keychain.selected_profile.customizable else true @@ -271,7 +275,7 @@ func _on_shortcut_tree_button_clicked(item: TreeItem, _column: int, id: int, _mb rect.position.y += 42 - tree.get_scroll().y rect.position += global_position rect.size = Vector2(110, 23 * shortcut_type_menu.get_item_count()) - shortcut_type_menu.popup(rect) + shortcut_type_menu.popup_on_parent(rect) elif id == 1: # Delete Keychain.action_erase_events(action) Keychain.selected_profile.change_action(action) diff --git a/game/addons/keychain/ShortcutEdit.tscn b/game/addons/keychain/ShortcutEdit.tscn index 57bfd46c..2e46a0e5 100644 --- a/game/addons/keychain/ShortcutEdit.tscn +++ b/game/addons/keychain/ShortcutEdit.tscn @@ -35,25 +35,30 @@ text = "Shortcut profile:" [node name="ProfileOptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 +size_flags_horizontal = 3 mouse_default_cursor_shape = 2 [node name="NewProfile" type="Button" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 +size_flags_horizontal = 3 mouse_default_cursor_shape = 2 text = "New" [node name="RenameProfile" type="Button" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 +size_flags_horizontal = 3 mouse_default_cursor_shape = 2 text = "Rename" [node name="DeleteProfile" type="Button" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 +size_flags_horizontal = 3 mouse_default_cursor_shape = 2 text = "Delete" [node name="OpenProfileFolder" type="Button" parent="VBoxContainer/HBoxContainer"] layout_mode = 2 +size_flags_horizontal = 3 mouse_default_cursor_shape = 2 text = "Open Folder" diff --git a/game/addons/keychain/ShortcutProfile.gd b/game/addons/keychain/ShortcutProfile.gd index 0fb269a1..ee548744 100644 --- a/game/addons/keychain/ShortcutProfile.gd +++ b/game/addons/keychain/ShortcutProfile.gd @@ -17,6 +17,9 @@ func fill_bindings() -> void: bindings[action] = InputMap.action_get_events(action) unnecessary_actions.erase(action) for action in unnecessary_actions: + if Keychain.keep_binding_check.call(action): + unnecessary_actions.erase(action) + continue bindings.erase(action) save() diff --git a/game/addons/keychain/assets/add.svg b/game/addons/keychain/assets/add.svg index afad08a2..b0fdf8a3 100644 --- a/game/addons/keychain/assets/add.svg +++ b/game/addons/keychain/assets/add.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/close.svg b/game/addons/keychain/assets/close.svg index 331727ab..fc587082 100644 --- a/game/addons/keychain/assets/close.svg +++ b/game/addons/keychain/assets/close.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/edit.svg b/game/addons/keychain/assets/edit.svg index 6fc7ae01..925f7930 100644 --- a/game/addons/keychain/assets/edit.svg +++ b/game/addons/keychain/assets/edit.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/folder.svg b/game/addons/keychain/assets/folder.svg index c2def257..904f94b2 100644 --- a/game/addons/keychain/assets/folder.svg +++ b/game/addons/keychain/assets/folder.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/keyboard.svg b/game/addons/keychain/assets/keyboard.svg index b9dfab71..ab6755c8 100644 --- a/game/addons/keychain/assets/keyboard.svg +++ b/game/addons/keychain/assets/keyboard.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/keyboard_physical.svg b/game/addons/keychain/assets/keyboard_physical.svg index 4364e0b4..f12cd461 100644 --- a/game/addons/keychain/assets/keyboard_physical.svg +++ b/game/addons/keychain/assets/keyboard_physical.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/mouse.svg b/game/addons/keychain/assets/mouse.svg index 21751208..2dcc03e1 100644 --- a/game/addons/keychain/assets/mouse.svg +++ b/game/addons/keychain/assets/mouse.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/assets/shortcut.svg b/game/addons/keychain/assets/shortcut.svg index 4ef16f04..87b1a40e 100644 --- a/game/addons/keychain/assets/shortcut.svg +++ b/game/addons/keychain/assets/shortcut.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/game/addons/keychain/translations/Translations.pot b/game/addons/keychain/translations/Translations.pot index 1f10c772..752a8c8c 100644 --- a/game/addons/keychain/translations/Translations.pot +++ b/game/addons/keychain/translations/Translations.pot @@ -103,10 +103,10 @@ msgstr "" msgid "Wheel Right Button" msgstr "" -msgid "X Button 1" +msgid "Mouse Thumb Button 1" msgstr "" -msgid "X Button 2" +msgid "Mouse Thumb Button 2" msgstr "" msgid "DualShock Cross, Xbox A, Nintendo B" diff --git a/game/addons/keychain/translations/el_GR.po b/game/addons/keychain/translations/el_GR.po index 4b6ce8bb..1ac87482 100644 --- a/game/addons/keychain/translations/el_GR.po +++ b/game/addons/keychain/translations/el_GR.po @@ -109,11 +109,11 @@ msgstr "Τρόχος αριστερά" msgid "Wheel Right Button" msgstr "Τρόχος δεξιά" -msgid "X Button 1" -msgstr "Κουμπί X 1" +msgid "Mouse Thumb Button 1" +msgstr "Κουμπί αντίχειρα ποντικού 1" -msgid "X Button 2" -msgstr "Κουμπί X 2" +msgid "Mouse Thumb Button 2" +msgstr "Κουμπί αντίχειρα ποντικιού 2" msgid "DualShock Cross, Xbox A, Nintendo B" msgstr "DualShock Σταυρός, Xbox A, Nintendo B" diff --git a/game/assets/localisation/locales/helpers.csv b/game/assets/localisation/locales/helpers.csv new file mode 100644 index 00000000..76853cc3 --- /dev/null +++ b/game/assets/localisation/locales/helpers.csv @@ -0,0 +1,2 @@ +keys;en;fr;de;es +Mapmode %s;Mapmode %s;Mode De Carte %s;Kartenmodus %s;Modo Mapa %s \ No newline at end of file diff --git a/game/assets/localisation/locales/helpers.csv.import b/game/assets/localisation/locales/helpers.csv.import new file mode 100644 index 00000000..2a5dbb39 --- /dev/null +++ b/game/assets/localisation/locales/helpers.csv.import @@ -0,0 +1,17 @@ +[remap] + +importer="csv_translation" +type="Translation" +uid="uid://dfro7ae4a6gkc" + +[deps] + +files=["res://assets/localisation/locales/helpers.en.translation", "res://assets/localisation/locales/helpers.fr.translation", "res://assets/localisation/locales/helpers.de.translation", "res://assets/localisation/locales/helpers.es.translation"] + +source_file="res://assets/localisation/locales/helpers.csv" +dest_files=["res://assets/localisation/locales/helpers.en.translation", "res://assets/localisation/locales/helpers.fr.translation", "res://assets/localisation/locales/helpers.de.translation", "res://assets/localisation/locales/helpers.es.translation"] + +[params] + +compress=false +delimiter=1 diff --git a/game/project.godot b/game/project.godot index 13883395..cd3e6641 100644 --- a/game/project.godot +++ b/game/project.godot @@ -95,13 +95,13 @@ map_west={ map_zoom_in={ "deadzone": 0.5, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":8,"position":Vector2(174, 17),"global_position":Vector2(180, 80),"factor":1.0,"button_index":4,"canceled":false,"pressed":true,"double_click":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":81,"physical_keycode":0,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":81,"physical_keycode":0,"key_label":0,"unicode":113,"location":0,"echo":false,"script":null) ] } map_zoom_out={ "deadzone": 0.5, "events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":16,"position":Vector2(325, 24),"global_position":Vector2(331, 87),"factor":1.0,"button_index":5,"canceled":false,"pressed":true,"double_click":false,"script":null) -, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":69,"physical_keycode":0,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":69,"physical_keycode":0,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null) ] } map_drag={ @@ -145,6 +145,7 @@ menu_pause={ [internationalization] locale/translation_remaps={} +locale/translations=PackedStringArray("res://assets/localisation/locales/helpers.de.translation", "res://assets/localisation/locales/helpers.en.translation", "res://assets/localisation/locales/helpers.es.translation", "res://assets/localisation/locales/helpers.fr.translation") locale/fallback="en_GB" locale/locale_filter_mode=0 locale/country_short_name={ diff --git a/game/src/Game/GameSession/GameSession.gd b/game/src/Game/GameSession/GameSession.gd index 9d07fd6a..1fac9d5c 100644 --- a/game/src/Game/GameSession/GameSession.gd +++ b/game/src/Game/GameSession/GameSession.gd @@ -13,6 +13,8 @@ func _ready() -> void: MusicConductor.generate_playlist() MusicConductor.select_next_song() + Keychain.initialize_profiles() + func _process(_delta : float) -> void: GameSingleton.update_clock() diff --git a/game/src/Game/GameStart.gd b/game/src/Game/GameStart.gd index 837e7127..a2be2f14 100644 --- a/game/src/Game/GameStart.gd +++ b/game/src/Game/GameStart.gd @@ -16,6 +16,32 @@ var _settings_base_path : String = "" var _compatibility_path_list : PackedStringArray = [] func _ready() -> void: + Keychain.actions = { + # Map Group + &"map_north": Keychain.InputAction.new("Move North", "Map", true), + &"map_east": Keychain.InputAction.new("Move East", "Map", true), + &"map_south": Keychain.InputAction.new("Move South", "Map", true), + &"map_west": Keychain.InputAction.new("Move West", "Map", true), + &"map_zoom_in": Keychain.InputAction.new("Zoom In", "Map", true), + &"map_zoom_out": Keychain.InputAction.new("Zoom Out", "Map", true), + &"map_drag": Keychain.InputAction.new("Mouse Drag", "Map", true), + &"map_click": Keychain.InputAction.new("Mouse Click", "Map", true), + &"map_right_click": Keychain.InputAction.new("Mouse Right Click", "Map", true), + # Time Group + &"time_pause": Keychain.InputAction.new("Pause", "Time", true), + &"time_speed_increase": Keychain.InputAction.new("Speed Increase", "Time", true), + &"time_speed_decrease": Keychain.InputAction.new("Speed Decrease", "Time", true), + # UI Group + &"menu_pause": Keychain.InputAction.new("Open Pause Menu", "UI", true), + } + + Keychain.groups = { + "Map": Keychain.InputGroup.new("", false), + "Time": Keychain.InputGroup.new("", false), + "UI": Keychain.InputGroup.new("", false), + "Hotkeys": Keychain.InputGroup.new("UI") + } + Localisation.initialize() if ArgumentParser.get_argument(&"help"): ArgumentParser._print_help() diff --git a/game/src/Game/Menu/OptionMenu/OptionsMenu.gd b/game/src/Game/Menu/OptionMenu/OptionsMenu.gd index 2b70d1ca..c487fb46 100644 --- a/game/src/Game/Menu/OptionMenu/OptionsMenu.gd +++ b/game/src/Game/Menu/OptionMenu/OptionsMenu.gd @@ -14,6 +14,29 @@ func _ready() -> void: _tab_container.set_tab_title(3, "OPTIONS_CONTROLS") _tab_container.set_tab_title(4, "OPTIONS_OTHER") + # Setup Keychain for Hotkeys + for action : StringName in InputMap.get_actions(): + if not Keychain.keep_binding_check.call(action): + continue + Keychain.actions[action] = Keychain.InputAction.new( + action.erase(0, "button_".length()).left(-"_hotkey".length()).capitalize(), + "Hotkeys") + var display_name : String = Keychain.actions[action].display_name + if display_name.begins_with("Mapmode"): + var mapmode_index := display_name.replace("Mapmode", "").to_int() + display_name = tr(GameSingleton.get_mapmode_localisation_key(mapmode_index)) + if mapmode_index <= 10: + display_name = display_name\ + .replace(" Mapmode", "")\ + .replace("Mode de carte ", "")\ + .replace("-Kartenmodus", "")\ + .replace("Modo de mapa de ", "")\ + .replace("Modo mapa de ", "")\ + .replace("Modo mapa ", "") + display_name = tr("Mapmode %s") % display_name.capitalize() + Keychain.actions[action].display_name = display_name + Keychain.profiles[0].bindings[action] = InputMap.action_get_events(action) + # Prepare options menu before loading user settings var tab_bar : TabBar = _tab_container.get_child(0, true)