diff --git a/addons/README.md b/addons/README.md
index 199147be3..b4067070d 100644
--- a/addons/README.md
+++ b/addons/README.md
@@ -1,5 +1,11 @@
# Addons
+## Keychain
+
+- Upstream: https://github.com/Orama-Interactive/Keychain
+- Version: Based on git commit 751540203124426947d69bd81f50fea07eded91d with slight modifications in Keychain.gd and ShortcutEdit.tscn.
+- License: [MIT](https://github.com/Orama-Interactive/Keychain/blob/main/LICENSE)
+
## gdgifexporter
- Upstream: https://github.com/jegor377/godot-gdgifexporter
diff --git a/addons/keychain/Keychain.gd b/addons/keychain/Keychain.gd
new file mode 100644
index 000000000..e934d6e7c
--- /dev/null
+++ b/addons/keychain/Keychain.gd
@@ -0,0 +1,216 @@
+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.
+var actions := {}
+# Syntax: "Group Name": InputGroup.new("Parent Group Name")
+var groups := {}
+var ignore_actions := []
+var ignore_ui_actions := true
+var changeable_types := [true, true, true, true]
+var multiple_menu_accelerators := false
+var config_path := "user://cache.ini"
+var config_file: ConfigFile
+
+
+class InputAction:
+ var display_name := ""
+ var group := ""
+ var global := true
+
+ func _init(_display_name := "", _group := "", _global := true) -> void:
+ 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:
+ ._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_scancode_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):
+ 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_scancode_with_modifiers():
+ return true
+ node.emit_signal("id_pressed", menu_item_id)
+ return true
+ if event.is_action(action) and echo:
+ if event.is_echo():
+ node.emit_signal("id_pressed", menu_item_id)
+ return true
+
+ return false
+
+
+class InputGroup:
+ var parent_group := ""
+ var folded := true
+ var tree_item: TreeItem
+
+ func _init(_parent_group := "", _folded := true) -> void:
+ parent_group = _parent_group
+ folded = _folded
+
+
+func _ready() -> void:
+ if !config_file:
+ config_file = ConfigFile.new()
+ if !config_path.empty():
+ config_file.load(config_path)
+
+ set_process_input(multiple_menu_accelerators)
+
+ # Load shortcut profiles
+ var profile_dir := Directory.new()
+ profile_dir.make_dir(PROFILES_PATH)
+ profile_dir.open(PROFILES_PATH)
+ profile_dir.list_dir_begin()
+ 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.plus_file(file_name))
+ if file is ShortcutProfile:
+ profiles.append(file)
+ file_name = profile_dir.get_next()
+
+ # If there are no profiles besides the default, create one custom
+ if profiles.size() == 1:
+ var profile := ShortcutProfile.new()
+ profile.name = "Custom"
+ profile.resource_path = PROFILES_PATH.plus_file("custom.tres")
+ var saved := profile.save()
+ if saved:
+ profiles.append(profile)
+
+ for profile in profiles:
+ profile.fill_bindings()
+
+ var l18n_dir := Directory.new()
+ l18n_dir.open(TRANSLATIONS_PATH)
+ l18n_dir.list_dir_begin()
+ 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.plus_file(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:
+ # Below line has been modified
+ input_action.get_node(Global.top_menu_container.get_node("MenuItems"))
+
+
+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():
+ index = profiles.size() - 1
+ profile_index = index
+ selected_profile = profiles[index]
+ for action in selected_profile.bindings:
+ action_erase_events(action)
+ for event in selected_profile.bindings[action]:
+ action_add_event(action, event)
+ # NOTE: Following line not present in the plugin itself, be careful not to overwrite
+ Global.update_hint_tooltips()
+
+
+func action_add_event(action: String, 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:
+ InputMap.action_erase_event(action, event)
+ if action in actions:
+ actions[action].update_node(action)
+
+
+func action_erase_events(action: String) -> 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.get_action_list(action)
+ for event in events:
+ if event is InputEventKey:
+ first_key = event
+ break
+ return first_key
diff --git a/addons/keychain/LICENSE b/addons/keychain/LICENSE
new file mode 100644
index 000000000..5deb9a77d
--- /dev/null
+++ b/addons/keychain/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Orama Interactive
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/addons/keychain/ShortcutEdit.gd b/addons/keychain/ShortcutEdit.gd
new file mode 100644
index 000000000..9eecf8107
--- /dev/null
+++ b/addons/keychain/ShortcutEdit.gd
@@ -0,0 +1,410 @@
+extends Control
+
+enum { KEYBOARD, MOUSE, JOY_BUTTON, JOY_AXIS }
+
+const MOUSE_BUTTON_NAMES := [
+ "Left Button",
+ "Right Button",
+ "Middle Button",
+ "Wheel Up Button",
+ "Wheel Down Button",
+ "Wheel Left Button",
+ "Wheel Right Button",
+ "X Button 1",
+ "X Button 2",
+]
+
+const JOY_BUTTON_NAMES := [
+ "DualShock Cross, Xbox A, Nintendo B",
+ "DualShock Circle, Xbox B, Nintendo A",
+ "DualShock Square, Xbox X, Nintendo Y",
+ "DualShock Triangle, Xbox Y, Nintendo X",
+ "L, L1",
+ "R, R1",
+ "L2",
+ "R2",
+ "L3",
+ "R3",
+ "Select, DualShock Share, Nintendo -",
+ "Start, DualShock Options, Nintendo +",
+ "D-Pad Up",
+ "D-Pad Down",
+ "D-Pad Left",
+ "D-Pad Right",
+ "Home, DualShock PS, Guide",
+ "Xbox Share, PS5 Microphone, Nintendo Capture",
+ "Xbox Paddle 1",
+ "Xbox Paddle 2",
+ "Xbox Paddle 3",
+ "Xbox Paddle 4",
+ "PS4/5 Touchpad",
+]
+
+const JOY_AXIS_NAMES := [
+ "(Left Stick Left)",
+ "(Left Stick Right)",
+ "(Left Stick Up)",
+ "(Left Stick Down)",
+ "(Right Stick Left)",
+ "(Right Stick Right)",
+ "(Right Stick Up)",
+ "(Right Stick Down)",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "(L2)",
+ "",
+ "(R2)",
+ "",
+ "",
+ "",
+ "",
+]
+
+var currently_editing_tree_item: TreeItem
+var is_editing := false
+# Textures taken from Godot https://github.com/godotengine/godot/tree/master/editor/icons
+var add_tex: Texture = preload("assets/add.svg")
+var edit_tex: Texture = preload("assets/edit.svg")
+var delete_tex: Texture = preload("assets/close.svg")
+var joy_axis_tex: Texture = preload("assets/joy_axis.svg")
+var joy_button_tex: Texture = preload("assets/joy_button.svg")
+var key_tex: Texture = preload("assets/keyboard.svg")
+var key_phys_tex: Texture = preload("assets/keyboard_physical.svg")
+var mouse_tex: Texture = preload("assets/mouse.svg")
+var shortcut_tex: Texture = preload("assets/shortcut.svg")
+var folder_tex: Texture = preload("assets/folder.svg")
+
+onready var tree: Tree = $VBoxContainer/ShortcutTree
+onready var profile_option_button: OptionButton = find_node("ProfileOptionButton")
+onready var rename_profile_button: Button = find_node("RenameProfile")
+onready var delete_profile_button: Button = find_node("DeleteProfile")
+onready var shortcut_type_menu: PopupMenu = $ShortcutTypeMenu
+onready var keyboard_shortcut_selector: ConfirmationDialog = $KeyboardShortcutSelectorDialog
+onready var mouse_shortcut_selector: ConfirmationDialog = $MouseShortcutSelectorDialog
+onready var joy_key_shortcut_selector: ConfirmationDialog = $JoyKeyShortcutSelectorDialog
+onready var joy_axis_shortcut_selector: ConfirmationDialog = $JoyAxisShortcutSelectorDialog
+onready var profile_settings: ConfirmationDialog = $ProfileSettings
+onready var profile_name: LineEdit = $ProfileSettings/ProfileName
+onready var delete_confirmation: ConfirmationDialog = $DeleteConfirmation
+
+
+func _ready() -> void:
+ for profile in Keychain.profiles:
+ profile_option_button.add_item(profile.name)
+
+ _fill_selector_options()
+
+ # Remove input types that are not changeable
+ var i := 0
+ for type in Keychain.changeable_types:
+ if !type:
+ shortcut_type_menu.remove_item(i)
+ else:
+ i += 1
+
+ profile_option_button.select(Keychain.profile_index)
+ _on_ProfileOptionButton_item_selected(Keychain.profile_index)
+
+
+func _construct_tree() -> void:
+ var buttons_disabled := false if Keychain.selected_profile.customizable else true
+ var tree_root: TreeItem = tree.create_item()
+ for group in Keychain.groups: # Create groups
+ var input_group: Keychain.InputGroup = Keychain.groups[group]
+ _create_group_tree_item(input_group, group)
+
+ for action in InputMap.get_actions(): # Fill the tree with actions and their events
+ if action in Keychain.ignore_actions:
+ continue
+ if Keychain.ignore_ui_actions and action.begins_with("ui_"):
+ continue
+
+ var display_name := get_action_name(action)
+ var group_name := ""
+ if action in Keychain.actions:
+ var input_action: Keychain.InputAction = Keychain.actions[action]
+ group_name = input_action.group
+
+ var tree_item: TreeItem
+ if group_name and group_name in Keychain.groups:
+ var input_group: Keychain.InputGroup = Keychain.groups[group_name]
+ var group_root: TreeItem = input_group.tree_item
+ tree_item = tree.create_item(group_root)
+
+ else:
+ tree_item = tree.create_item(tree_root)
+
+ tree_item.set_text(0, display_name)
+ tree_item.set_metadata(0, action)
+ tree_item.set_icon(0, shortcut_tex)
+ for event in InputMap.get_action_list(action):
+ add_event_tree_item(event, tree_item)
+
+ tree_item.add_button(0, add_tex, 0, buttons_disabled, "Add")
+ tree_item.add_button(0, delete_tex, 1, buttons_disabled, "Delete")
+ tree_item.collapsed = true
+
+
+func _fill_selector_options() -> void:
+ keyboard_shortcut_selector.entered_shortcut.visible = true
+ keyboard_shortcut_selector.option_button.visible = false
+ mouse_shortcut_selector.input_type_l.text = "Mouse Button Index:"
+ joy_key_shortcut_selector.input_type_l.text = "Joypad Button Index:"
+ joy_axis_shortcut_selector.input_type_l.text = "Joypad Axis Index:"
+
+ var mouse_option_button: OptionButton = mouse_shortcut_selector.option_button
+ for option in MOUSE_BUTTON_NAMES:
+ mouse_option_button.add_item(option)
+
+ var joy_key_option_button: OptionButton = joy_key_shortcut_selector.option_button
+ for i in JOY_BUTTON_MAX:
+ var text: String = tr("Button") + " %s" % i
+ if i < JOY_BUTTON_NAMES.size():
+ text += " (%s)" % tr(JOY_BUTTON_NAMES[i])
+ joy_key_option_button.add_item(text)
+
+ var joy_axis_option_button: OptionButton = joy_axis_shortcut_selector.option_button
+ var i := 0.0
+ for option in JOY_AXIS_NAMES:
+ var sign_symbol = "+" if floor(i) != i else "-"
+ var text: String = tr("Axis") + " %s %s %s" % [floor(i), sign_symbol, tr(option)]
+ joy_axis_option_button.add_item(text)
+ i += 0.5
+
+
+func _create_group_tree_item(group: Keychain.InputGroup, group_name: String) -> void:
+ if group.tree_item:
+ return
+
+ var group_root: TreeItem
+ if group.parent_group:
+ var parent_group: Keychain.InputGroup = Keychain.groups[group.parent_group]
+ _create_group_tree_item(parent_group, group.parent_group)
+ group_root = tree.create_item(parent_group.tree_item)
+ else:
+ group_root = tree.create_item(tree.get_root())
+ group_root.set_text(0, group_name)
+ group_root.set_icon(0, folder_tex)
+ group.tree_item = group_root
+ if group.folded:
+ group_root.collapsed = true
+
+
+func get_action_name(action: String) -> String:
+ var display_name := ""
+ if action in Keychain.actions:
+ display_name = Keychain.actions[action].display_name
+
+ if display_name.empty():
+ display_name = _humanize_snake_case(action)
+ return display_name
+
+
+func _humanize_snake_case(text: String) -> String:
+ text = text.replace("_", " ")
+ var first_letter := text.left(1)
+ first_letter = first_letter.capitalize()
+ text.erase(0, 1)
+ text = text.insert(0, first_letter)
+ return text
+
+
+func add_event_tree_item(event: InputEvent, action_tree_item: TreeItem) -> void:
+ var event_class := event.get_class()
+ match event_class:
+ "InputEventKey":
+ if !Keychain.changeable_types[0]:
+ return
+ "InputEventMouseButton":
+ if !Keychain.changeable_types[1]:
+ return
+ "InputEventJoypadButton":
+ if !Keychain.changeable_types[2]:
+ return
+ "InputEventJoypadMotion":
+ if !Keychain.changeable_types[3]:
+ return
+
+ var buttons_disabled := false if Keychain.selected_profile.customizable else true
+ var event_tree_item: TreeItem = tree.create_item(action_tree_item)
+ event_tree_item.set_text(0, event_to_str(event))
+ event_tree_item.set_metadata(0, event)
+ match event_class:
+ "InputEventKey":
+ var scancode: int = event.get_scancode_with_modifiers()
+ if scancode > 0:
+ event_tree_item.set_icon(0, key_tex)
+ else:
+ event_tree_item.set_icon(0, key_phys_tex)
+ "InputEventMouseButton":
+ event_tree_item.set_icon(0, mouse_tex)
+ "InputEventJoypadButton":
+ event_tree_item.set_icon(0, joy_button_tex)
+ "InputEventJoypadMotion":
+ event_tree_item.set_icon(0, joy_axis_tex)
+ event_tree_item.add_button(0, edit_tex, 0, buttons_disabled, "Edit")
+ event_tree_item.add_button(0, delete_tex, 1, buttons_disabled, "Delete")
+
+
+func event_to_str(event: InputEvent) -> String:
+ var output := ""
+ if event is InputEventKey:
+ var scancode: int = event.get_scancode_with_modifiers()
+ var physical_str := ""
+ if scancode == 0:
+ scancode = event.get_physical_scancode_with_modifiers()
+ physical_str = " " + tr("(Physical)")
+ output = OS.get_scancode_string(scancode) + physical_str
+
+ elif event is InputEventMouseButton:
+ output = tr(MOUSE_BUTTON_NAMES[event.button_index - 1])
+
+ elif event is InputEventJoypadButton:
+ var button_index: int = event.button_index
+ output = tr("Button")
+ if button_index >= JOY_BUTTON_NAMES.size():
+ output += " %s" % button_index
+ else:
+ output += " %s (%s)" % [button_index, tr(JOY_BUTTON_NAMES[button_index])]
+
+ elif event is InputEventJoypadMotion:
+ var positive_axis: bool = event.axis_value > 0
+ var axis_value: int = event.axis * 2 + int(positive_axis)
+ var sign_symbol = "+" if positive_axis else "-"
+ output = tr("Axis")
+ output += " %s %s %s" % [event.axis, sign_symbol, tr(JOY_AXIS_NAMES[axis_value])]
+ return output
+
+
+func _on_ShortcutTree_button_pressed(item: TreeItem, _column: int, id: int) -> void:
+ var action = item.get_metadata(0)
+ currently_editing_tree_item = item
+ if action is String:
+ if id == 0: # Add
+ var rect: Rect2 = tree.get_item_area_rect(item, 0)
+ rect.position.x = rect.end.x - 42
+ rect.position.y += 42 - tree.get_scroll().y
+ rect.position += rect_global_position
+ rect.size = Vector2(110, 23 * shortcut_type_menu.get_item_count())
+ shortcut_type_menu.popup(rect)
+ elif id == 1: # Delete
+ Keychain.action_erase_events(action)
+ Keychain.selected_profile.change_action(action)
+ var child := item.get_children()
+ while child != null:
+ child.free()
+ child = item.get_children()
+
+ elif action is InputEvent:
+ var parent_action = item.get_parent().get_metadata(0)
+ if id == 0: # Edit
+ if action is InputEventKey:
+ keyboard_shortcut_selector.popup_centered()
+ elif action is InputEventMouseButton:
+ mouse_shortcut_selector.popup_centered()
+ elif action is InputEventJoypadButton:
+ joy_key_shortcut_selector.popup_centered()
+ elif action is InputEventJoypadMotion:
+ joy_axis_shortcut_selector.popup_centered()
+ elif id == 1: # Delete
+ if not parent_action is String:
+ return
+ Keychain.action_erase_event(parent_action, action)
+ Keychain.selected_profile.change_action(parent_action)
+ item.free()
+
+
+func _on_ShortcutTree_item_activated() -> void:
+ var selected_item: TreeItem = tree.get_selected()
+ if selected_item.get_button_count(0) > 0 and !selected_item.is_button_disabled(0, 0):
+ _on_ShortcutTree_button_pressed(tree.get_selected(), 0, 0)
+
+
+func _on_ShortcutTypeMenu_id_pressed(id: int) -> void:
+ if id == KEYBOARD:
+ keyboard_shortcut_selector.popup_centered()
+ elif id == MOUSE:
+ mouse_shortcut_selector.popup_centered()
+ elif id == JOY_BUTTON:
+ joy_key_shortcut_selector.popup_centered()
+ elif id == JOY_AXIS:
+ joy_axis_shortcut_selector.popup_centered()
+
+
+func _on_ProfileOptionButton_item_selected(index: int) -> void:
+ Keychain.change_profile(index)
+ rename_profile_button.disabled = false if Keychain.selected_profile.customizable else true
+ delete_profile_button.disabled = false if Keychain.selected_profile.customizable else true
+
+ # Re-construct the tree
+ for group in Keychain.groups:
+ Keychain.groups[group].tree_item = null
+ tree.clear()
+ _construct_tree()
+ Keychain.config_file.set_value("shortcuts", "shortcuts_profile", index)
+ Keychain.config_file.save(Keychain.config_path)
+
+
+func _on_NewProfile_pressed() -> void:
+ is_editing = false
+ profile_name.text = "New Shortcut Profile"
+ profile_settings.window_title = "New Shortcut Profile"
+ profile_settings.popup_centered()
+
+
+func _on_RenameProfile_pressed() -> void:
+ is_editing = true
+ profile_name.text = Keychain.selected_profile.name
+ profile_settings.window_title = "Rename Shortcut Profile"
+ profile_settings.popup_centered()
+
+
+func _on_DeleteProfile_pressed() -> void:
+ delete_confirmation.popup_centered()
+
+
+func _on_OpenProfileFolder_pressed() -> void:
+ OS.shell_open(ProjectSettings.globalize_path(Keychain.PROFILES_PATH))
+
+
+func _on_ProfileSettings_confirmed() -> void:
+ var file_name := profile_name.text + ".tres"
+ var profile := ShortcutProfile.new()
+ profile.name = profile_name.text
+ profile.resource_path = Keychain.PROFILES_PATH.plus_file(file_name)
+ profile.fill_bindings()
+ var saved := profile.save()
+ if not saved:
+ return
+
+ if is_editing:
+ var old_file_name: String = Keychain.selected_profile.resource_path
+ if old_file_name != file_name:
+ _delete_profile_file(old_file_name)
+ Keychain.profiles[Keychain.profile_index] = profile
+ profile_option_button.set_item_text(Keychain.profile_index, profile.name)
+ else: # Add new shortcut profile
+ Keychain.profiles.append(profile)
+ profile_option_button.add_item(profile.name)
+ Keychain.profile_index = Keychain.profiles.size() - 1
+ profile_option_button.select(Keychain.profile_index)
+ _on_ProfileOptionButton_item_selected(Keychain.profile_index)
+
+
+func _delete_profile_file(file_name: String) -> void:
+ var dir := Directory.new()
+ dir.remove(file_name)
+
+
+func _on_DeleteConfirmation_confirmed() -> void:
+ _delete_profile_file(Keychain.selected_profile.resource_path)
+ profile_option_button.remove_item(Keychain.profile_index)
+ Keychain.profiles.remove(Keychain.profile_index)
+ Keychain.profile_index -= 1
+ if Keychain.profile_index < 0:
+ Keychain.profile_index = 0
+ profile_option_button.select(Keychain.profile_index)
+ _on_ProfileOptionButton_item_selected(Keychain.profile_index)
diff --git a/addons/keychain/ShortcutEdit.tscn b/addons/keychain/ShortcutEdit.tscn
new file mode 100644
index 000000000..0dbd150c2
--- /dev/null
+++ b/addons/keychain/ShortcutEdit.tscn
@@ -0,0 +1,114 @@
+[gd_scene load_steps=7 format=2]
+
+[ext_resource path="res://addons/keychain/ShortcutEdit.gd" type="Script" id=1]
+[ext_resource path="res://addons/keychain/assets/joy_button.svg" type="Texture" id=2]
+[ext_resource path="res://addons/keychain/assets/keyboard.svg" type="Texture" id=3]
+[ext_resource path="res://addons/keychain/assets/joy_axis.svg" type="Texture" id=4]
+[ext_resource path="res://addons/keychain/assets/mouse.svg" type="Texture" id=5]
+[ext_resource path="res://addons/keychain/ShortcutSelectorDialog.tscn" type="PackedScene" id=6]
+
+[node name="ShortcutEdit" type="VBoxContainer"]
+anchor_right = 1.0
+anchor_bottom = 1.0
+size_flags_vertical = 3
+script = ExtResource( 1 )
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+margin_right = 1280.0
+margin_bottom = 720.0
+size_flags_vertical = 3
+
+[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+margin_right = 1280.0
+margin_bottom = 20.0
+
+[node name="ProfileLabel" type="Label" parent="VBoxContainer/HBoxContainer"]
+margin_top = 3.0
+margin_right = 102.0
+margin_bottom = 17.0
+text = "Shortcut profile:"
+
+[node name="ProfileOptionButton" type="OptionButton" parent="VBoxContainer/HBoxContainer"]
+margin_left = 106.0
+margin_right = 135.0
+margin_bottom = 20.0
+mouse_default_cursor_shape = 2
+
+[node name="NewProfile" type="Button" parent="VBoxContainer/HBoxContainer"]
+margin_left = 139.0
+margin_right = 179.0
+margin_bottom = 20.0
+mouse_default_cursor_shape = 2
+text = "New"
+
+[node name="RenameProfile" type="Button" parent="VBoxContainer/HBoxContainer"]
+margin_left = 183.0
+margin_right = 247.0
+margin_bottom = 20.0
+mouse_default_cursor_shape = 2
+text = "Rename"
+
+[node name="DeleteProfile" type="Button" parent="VBoxContainer/HBoxContainer"]
+margin_left = 251.0
+margin_right = 306.0
+margin_bottom = 20.0
+mouse_default_cursor_shape = 2
+text = "Delete"
+
+[node name="OpenProfileFolder" type="Button" parent="VBoxContainer/HBoxContainer"]
+margin_left = 310.0
+margin_right = 401.0
+margin_bottom = 20.0
+mouse_default_cursor_shape = 2
+text = "Open Folder"
+
+[node name="ShortcutTree" type="Tree" parent="VBoxContainer"]
+margin_top = 24.0
+margin_right = 1280.0
+margin_bottom = 720.0
+size_flags_vertical = 3
+hide_root = true
+
+[node name="ShortcutTypeMenu" type="PopupMenu" parent="."]
+margin_right = 20.0
+margin_bottom = 20.0
+items = [ "Key", ExtResource( 3 ), 0, false, false, 0, 0, null, "", false, "Mouse Button", ExtResource( 5 ), 0, false, false, 1, 0, null, "", false, "Joy Button", ExtResource( 2 ), 0, false, false, 2, 0, null, "", false, "Joy Axis", ExtResource( 4 ), 0, false, false, 3, 0, null, "", false ]
+
+[node name="KeyboardShortcutSelectorDialog" parent="." instance=ExtResource( 6 )]
+
+[node name="MouseShortcutSelectorDialog" parent="." instance=ExtResource( 6 )]
+input_type = 1
+
+[node name="JoyKeyShortcutSelectorDialog" parent="." instance=ExtResource( 6 )]
+input_type = 2
+
+[node name="JoyAxisShortcutSelectorDialog" parent="." instance=ExtResource( 6 )]
+input_type = 3
+
+[node name="ProfileSettings" type="ConfirmationDialog" parent="."]
+margin_right = 200.0
+margin_bottom = 70.0
+
+[node name="ProfileName" type="LineEdit" parent="ProfileSettings"]
+margin_left = 8.0
+margin_top = 8.0
+margin_right = 192.0
+margin_bottom = 34.0
+caret_blink = true
+caret_blink_speed = 0.5
+
+[node name="DeleteConfirmation" type="ConfirmationDialog" parent="."]
+margin_right = 200.0
+margin_bottom = 70.0
+dialog_text = "Are you sure you want to delete this shortcut profile?"
+
+[connection signal="item_selected" from="VBoxContainer/HBoxContainer/ProfileOptionButton" to="." method="_on_ProfileOptionButton_item_selected"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/NewProfile" to="." method="_on_NewProfile_pressed"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/RenameProfile" to="." method="_on_RenameProfile_pressed"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/DeleteProfile" to="." method="_on_DeleteProfile_pressed"]
+[connection signal="pressed" from="VBoxContainer/HBoxContainer/OpenProfileFolder" to="." method="_on_OpenProfileFolder_pressed"]
+[connection signal="button_pressed" from="VBoxContainer/ShortcutTree" to="." method="_on_ShortcutTree_button_pressed"]
+[connection signal="item_activated" from="VBoxContainer/ShortcutTree" to="." method="_on_ShortcutTree_item_activated"]
+[connection signal="id_pressed" from="ShortcutTypeMenu" to="." method="_on_ShortcutTypeMenu_id_pressed"]
+[connection signal="confirmed" from="ProfileSettings" to="." method="_on_ProfileSettings_confirmed"]
+[connection signal="confirmed" from="DeleteConfirmation" to="." method="_on_DeleteConfirmation_confirmed"]
diff --git a/addons/keychain/ShortcutProfile.gd b/addons/keychain/ShortcutProfile.gd
new file mode 100644
index 000000000..af9d583c2
--- /dev/null
+++ b/addons/keychain/ShortcutProfile.gd
@@ -0,0 +1,31 @@
+class_name ShortcutProfile
+extends Resource
+
+export(String) var name := ""
+export(bool) var customizable := true
+export(Dictionary) var bindings := {}
+
+
+func _init() -> void:
+ bindings = bindings.duplicate(true)
+
+
+func fill_bindings() -> void:
+ for action in InputMap.get_actions():
+ if not action in bindings:
+ bindings[action] = InputMap.get_action_list(action)
+
+
+func change_action(action: String) -> void:
+ if not customizable:
+ return
+ bindings[action] = InputMap.get_action_list(action)
+ save()
+
+
+func save() -> bool:
+ var err := ResourceSaver.save(resource_path, self)
+ if err != OK:
+ print("Error saving shortcut profile %s. Error code: %s" % [resource_path, err])
+ return false
+ return true
diff --git a/addons/keychain/ShortcutSelectorDialog.gd b/addons/keychain/ShortcutSelectorDialog.gd
new file mode 100644
index 000000000..3e4e2d0cc
--- /dev/null
+++ b/addons/keychain/ShortcutSelectorDialog.gd
@@ -0,0 +1,190 @@
+extends ConfirmationDialog
+
+enum InputTypes { KEYBOARD, MOUSE, JOY_BUTTON, JOY_AXIS }
+
+export(InputTypes) var input_type: int = 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().focus_neighbour_top = entered_shortcut.get_path()
+ get_cancel().focus_neighbour_top = entered_shortcut.get_path()
+ entered_shortcut.focus_neighbour_bottom = get_ok().get_path()
+ else:
+ get_ok().focus_neighbour_top = option_button.get_path()
+ get_cancel().focus_neighbour_top = option_button.get_path()
+ option_button.focus_neighbour_bottom = get_ok().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_scancode_string(event.get_scancode_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 String: # 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 String: # 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: String, 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: String = 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.shortcut_match(metadata):
+ var map_action: String = tree_item.get_parent().get_metadata(0)
+ if map_action in Keychain.actions:
+ # If it's local, check if it's the same group, otherwise ignore
+ if !Keychain.actions[map_action].global:
+ if Keychain.actions[map_action].group != group:
+ tree_item = _get_next_tree_item(tree_item)
+ continue
+ 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_children():
+ current = current.get_children()
+ 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: String, 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.begins_with("ui_"):
+ continue
+ for map_event in InputMap.get_action_list(map_action):
+ if event.shortcut_match(map_event):
+ if map_action in Keychain.actions:
+ # If it's local, check if it's the same group, otherwise ignore
+ if !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 = ""
+ yield(get_tree(), "idle_frame")
+ entered_shortcut.grab_focus()
+ else:
+ if !listened_input:
+ _on_OptionButton_item_selected(0)
+ else:
+ _show_assigned_state(listened_input)
+
+
+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)
diff --git a/addons/keychain/ShortcutSelectorDialog.tscn b/addons/keychain/ShortcutSelectorDialog.tscn
new file mode 100644
index 000000000..0e4188dbe
--- /dev/null
+++ b/addons/keychain/ShortcutSelectorDialog.tscn
@@ -0,0 +1,49 @@
+[gd_scene load_steps=2 format=2]
+
+[ext_resource path="res://addons/keychain/ShortcutSelectorDialog.gd" type="Script" id=1]
+
+[node name="ShortcutSelectorDialog" type="ConfirmationDialog"]
+margin_right = 200.0
+margin_bottom = 70.0
+popup_exclusive = true
+window_title = "Set the shortcut"
+script = ExtResource( 1 )
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+margin_left = 8.0
+margin_top = 8.0
+margin_right = 341.0
+margin_bottom = 64.0
+
+[node name="InputTypeLabel" type="Label" parent="VBoxContainer"]
+margin_right = 333.0
+margin_bottom = 14.0
+text = "Press a key or a key combination to set the shortcut"
+
+[node name="EnteredShortcut" type="LineEdit" parent="VBoxContainer"]
+visible = false
+margin_top = 18.0
+margin_right = 333.0
+margin_bottom = 32.0
+align = 1
+editable = false
+virtual_keyboard_enabled = false
+
+[node name="OptionButton" type="OptionButton" parent="VBoxContainer"]
+margin_top = 18.0
+margin_right = 333.0
+margin_bottom = 38.0
+mouse_default_cursor_shape = 2
+
+[node name="AlreadyExistsLabel" type="Label" parent="VBoxContainer"]
+margin_top = 42.0
+margin_right = 333.0
+margin_bottom = 56.0
+align = 1
+
+[connection signal="about_to_show" from="." to="." method="_on_ShortcutSelectorDialog_about_to_show"]
+[connection signal="confirmed" from="." to="." method="_on_ShortcutSelectorDialog_confirmed"]
+[connection signal="popup_hide" from="." to="." method="_on_ShortcutSelectorDialog_popup_hide"]
+[connection signal="focus_entered" from="VBoxContainer/EnteredShortcut" to="." method="_on_EnteredShortcut_focus_entered"]
+[connection signal="focus_exited" from="VBoxContainer/EnteredShortcut" to="." method="_on_EnteredShortcut_focus_exited"]
+[connection signal="item_selected" from="VBoxContainer/OptionButton" to="." method="_on_OptionButton_item_selected"]
diff --git a/addons/keychain/assets/add.svg b/addons/keychain/assets/add.svg
new file mode 100644
index 000000000..afad08a2e
--- /dev/null
+++ b/addons/keychain/assets/add.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/add.svg.import b/addons/keychain/assets/add.svg.import
new file mode 100644
index 000000000..7437cee83
--- /dev/null
+++ b/addons/keychain/assets/add.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/add.svg-4084e314648c872072757f9b0f544cf9.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/add.svg"
+dest_files=[ "res://.import/add.svg-4084e314648c872072757f9b0f544cf9.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/close.svg b/addons/keychain/assets/close.svg
new file mode 100644
index 000000000..331727ab9
--- /dev/null
+++ b/addons/keychain/assets/close.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/close.svg.import b/addons/keychain/assets/close.svg.import
new file mode 100644
index 000000000..386cc2705
--- /dev/null
+++ b/addons/keychain/assets/close.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/close.svg-12d57fa7e5a34826f312eed6ba1feb1a.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/close.svg"
+dest_files=[ "res://.import/close.svg-12d57fa7e5a34826f312eed6ba1feb1a.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/edit.svg b/addons/keychain/assets/edit.svg
new file mode 100644
index 000000000..6fc7ae012
--- /dev/null
+++ b/addons/keychain/assets/edit.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/edit.svg.import b/addons/keychain/assets/edit.svg.import
new file mode 100644
index 000000000..97e414ccf
--- /dev/null
+++ b/addons/keychain/assets/edit.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/edit.svg-cd9834545a8696f1e8611efa12a48f33.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/edit.svg"
+dest_files=[ "res://.import/edit.svg-cd9834545a8696f1e8611efa12a48f33.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/folder.svg b/addons/keychain/assets/folder.svg
new file mode 100644
index 000000000..c2def257e
--- /dev/null
+++ b/addons/keychain/assets/folder.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/folder.svg.import b/addons/keychain/assets/folder.svg.import
new file mode 100644
index 000000000..6f338de44
--- /dev/null
+++ b/addons/keychain/assets/folder.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/folder.svg-490bb7e2d2aa4425de998b1d382cf534.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/folder.svg"
+dest_files=[ "res://.import/folder.svg-490bb7e2d2aa4425de998b1d382cf534.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/joy_axis.svg b/addons/keychain/assets/joy_axis.svg
new file mode 100644
index 000000000..1ab65f0af
--- /dev/null
+++ b/addons/keychain/assets/joy_axis.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/joy_axis.svg.import b/addons/keychain/assets/joy_axis.svg.import
new file mode 100644
index 000000000..bb97537f4
--- /dev/null
+++ b/addons/keychain/assets/joy_axis.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/joy_axis.svg-9e1156700cfe46afb1ca622536a9a2e1.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/joy_axis.svg"
+dest_files=[ "res://.import/joy_axis.svg-9e1156700cfe46afb1ca622536a9a2e1.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/joy_button.svg b/addons/keychain/assets/joy_button.svg
new file mode 100644
index 000000000..080d91ad5
--- /dev/null
+++ b/addons/keychain/assets/joy_button.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/joy_button.svg.import b/addons/keychain/assets/joy_button.svg.import
new file mode 100644
index 000000000..d5662ed2e
--- /dev/null
+++ b/addons/keychain/assets/joy_button.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/joy_button.svg-df5663c6f296cab556e81b9c770699d0.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/joy_button.svg"
+dest_files=[ "res://.import/joy_button.svg-df5663c6f296cab556e81b9c770699d0.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/keyboard.svg b/addons/keychain/assets/keyboard.svg
new file mode 100644
index 000000000..b9dfab71e
--- /dev/null
+++ b/addons/keychain/assets/keyboard.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/keyboard.svg.import b/addons/keychain/assets/keyboard.svg.import
new file mode 100644
index 000000000..a74b8cfda
--- /dev/null
+++ b/addons/keychain/assets/keyboard.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/keyboard.svg-fac365b6f70899f1dfa71ce4b4761ac6.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/keyboard.svg"
+dest_files=[ "res://.import/keyboard.svg-fac365b6f70899f1dfa71ce4b4761ac6.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/keyboard_physical.svg b/addons/keychain/assets/keyboard_physical.svg
new file mode 100644
index 000000000..4364e0b4f
--- /dev/null
+++ b/addons/keychain/assets/keyboard_physical.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/keyboard_physical.svg.import b/addons/keychain/assets/keyboard_physical.svg.import
new file mode 100644
index 000000000..d25f2aed7
--- /dev/null
+++ b/addons/keychain/assets/keyboard_physical.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/keyboard_physical.svg-f50c796569ade32b57ece1ba0bd7dfbb.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/keyboard_physical.svg"
+dest_files=[ "res://.import/keyboard_physical.svg-f50c796569ade32b57ece1ba0bd7dfbb.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/mouse.svg b/addons/keychain/assets/mouse.svg
new file mode 100644
index 000000000..217512085
--- /dev/null
+++ b/addons/keychain/assets/mouse.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/mouse.svg.import b/addons/keychain/assets/mouse.svg.import
new file mode 100644
index 000000000..ad38c3a71
--- /dev/null
+++ b/addons/keychain/assets/mouse.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/mouse.svg-559695787f3bb55c16dc66bd6a9b9032.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/mouse.svg"
+dest_files=[ "res://.import/mouse.svg-559695787f3bb55c16dc66bd6a9b9032.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/assets/shortcut.svg b/addons/keychain/assets/shortcut.svg
new file mode 100644
index 000000000..4ef16f040
--- /dev/null
+++ b/addons/keychain/assets/shortcut.svg
@@ -0,0 +1 @@
+
diff --git a/addons/keychain/assets/shortcut.svg.import b/addons/keychain/assets/shortcut.svg.import
new file mode 100644
index 000000000..c690e79ac
--- /dev/null
+++ b/addons/keychain/assets/shortcut.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/shortcut.svg-401daac18a817142e2b29e5801e00053.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/keychain/assets/shortcut.svg"
+dest_files=[ "res://.import/shortcut.svg-401daac18a817142e2b29e5801e00053.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/keychain/plugin.cfg b/addons/keychain/plugin.cfg
new file mode 100644
index 000000000..36bf2f63e
--- /dev/null
+++ b/addons/keychain/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="Keychain"
+description="A plugin for the Godot Engine that aims to give the player full control over the input actions of the game."
+author="Orama Interactive"
+version="1.1"
+script="plugin.gd"
diff --git a/addons/keychain/plugin.gd b/addons/keychain/plugin.gd
new file mode 100644
index 000000000..0eee623e3
--- /dev/null
+++ b/addons/keychain/plugin.gd
@@ -0,0 +1,10 @@
+tool
+extends EditorPlugin
+
+
+func _enter_tree() -> void:
+ add_autoload_singleton("Keychain", "res://addons/keychain/Keychain.gd")
+
+
+func _exit_tree() -> void:
+ remove_autoload_singleton("Keychain")
diff --git a/addons/keychain/profiles/default.tres b/addons/keychain/profiles/default.tres
new file mode 100644
index 000000000..ce74da485
--- /dev/null
+++ b/addons/keychain/profiles/default.tres
@@ -0,0 +1,10 @@
+[gd_resource type="Resource" load_steps=2 format=2]
+
+[ext_resource path="res://addons/keychain/ShortcutProfile.gd" type="Script" id=1]
+
+[resource]
+script = ExtResource( 1 )
+name = "Default"
+customizable = false
+bindings = {
+}
diff --git a/addons/keychain/translations/README.md b/addons/keychain/translations/README.md
new file mode 100644
index 000000000..086527b8f
--- /dev/null
+++ b/addons/keychain/translations/README.md
@@ -0,0 +1,4 @@
+# Localization files for Keychain
+Keychain uses .po files to handle localization. More information in the Godot documentation: [Localization using gettext](https://docs.godotengine.org/en/stable/tutorials/i18n/localization_using_gettext.html)
+
+Simply add a .po file of the language you want to provide localization for, and Keychain will automatically add it to the TranslationServer when the project runs.
diff --git a/addons/keychain/translations/Translations.pot b/addons/keychain/translations/Translations.pot
new file mode 100644
index 000000000..1f10c7720
--- /dev/null
+++ b/addons/keychain/translations/Translations.pot
@@ -0,0 +1,209 @@
+msgid ""
+msgstr ""
+
+msgid "OK"
+msgstr ""
+
+msgid "Cancel"
+msgstr ""
+
+msgid "Add"
+msgstr ""
+
+msgid "Edit"
+msgstr ""
+
+msgid "Delete"
+msgstr ""
+
+msgid "Shortcut profile:"
+msgstr ""
+
+msgid "Default"
+msgstr ""
+
+msgid "Custom"
+msgstr ""
+
+msgid "New"
+msgstr ""
+
+msgid "Rename"
+msgstr ""
+
+msgid "Open Folder"
+msgstr ""
+
+msgid "New Shortcut Profile"
+msgstr ""
+
+msgid "Rename Shortcut Profile"
+msgstr ""
+
+msgid "Are you sure you want to delete this shortcut profile?"
+msgstr ""
+
+msgid "Key"
+msgstr ""
+
+msgid "Mouse Button"
+msgstr ""
+
+msgid "Joy Button"
+msgstr ""
+
+msgid "Joy Axis"
+msgstr ""
+
+msgid "Button"
+msgstr ""
+
+msgid "Axis"
+msgstr ""
+
+msgid "(Physical)"
+msgstr ""
+
+msgid "Set the shortcut"
+msgstr ""
+
+msgid "Press a key or a key combination to set the shortcut"
+msgstr ""
+
+msgid "Mouse Button Index:"
+msgstr ""
+
+msgid "Joypad Button Index:"
+msgstr ""
+
+msgid "Joypad Axis Index:"
+msgstr ""
+
+msgid "Already assigned to: %s"
+msgstr ""
+
+msgid "Left Button"
+msgstr ""
+
+msgid "Right Button"
+msgstr ""
+
+msgid "Middle Button"
+msgstr ""
+
+msgid "Wheel Up Button"
+msgstr ""
+
+msgid "Wheel Down Button"
+msgstr ""
+
+msgid "Wheel Left Button"
+msgstr ""
+
+msgid "Wheel Right Button"
+msgstr ""
+
+msgid "X Button 1"
+msgstr ""
+
+msgid "X Button 2"
+msgstr ""
+
+msgid "DualShock Cross, Xbox A, Nintendo B"
+msgstr ""
+
+msgid "DualShock Circle, Xbox B, Nintendo A"
+msgstr ""
+
+msgid "DualShock Square, Xbox X, Nintendo Y"
+msgstr ""
+
+msgid "DualShock Triangle, Xbox Y, Nintendo X"
+msgstr ""
+
+msgid "L, L1"
+msgstr ""
+
+msgid "R, R1"
+msgstr ""
+
+msgid "L2"
+msgstr ""
+
+msgid "R2"
+msgstr ""
+
+msgid "L3"
+msgstr ""
+
+msgid "R3"
+msgstr ""
+
+msgid "Select, DualShock Share, Nintendo -"
+msgstr ""
+
+msgid "Start, DualShock Options, Nintendo +"
+msgstr ""
+
+msgid "D-Pad Up"
+msgstr ""
+
+msgid "D-Pad Down"
+msgstr ""
+
+msgid "D-Pad Left"
+msgstr ""
+
+msgid "D-Pad Right"
+msgstr ""
+
+msgid "Home, DualShock PS, Guide"
+msgstr ""
+
+msgid "Xbox Share, PS5 Microphone, Nintendo Capture"
+msgstr ""
+
+msgid "Xbox Paddle 1"
+msgstr ""
+
+msgid "Xbox Paddle 2"
+msgstr ""
+
+msgid "Xbox Paddle 3"
+msgstr ""
+
+msgid "Xbox Paddle 4"
+msgstr ""
+
+msgid "PS4/5 Touchpad"
+msgstr ""
+
+msgid "(Left Stick Left)"
+msgstr ""
+
+msgid "(Left Stick Right)"
+msgstr ""
+
+msgid "(Left Stick Up)"
+msgstr ""
+
+msgid "(Left Stick Down)"
+msgstr ""
+
+msgid "(Right Stick Left)"
+msgstr ""
+
+msgid "(Right Stick Right)"
+msgstr ""
+
+msgid "(Right Stick Up)"
+msgstr ""
+
+msgid "(Right Stick Down)"
+msgstr ""
+
+msgid "(L2)"
+msgstr ""
+
+msgid "(R2)"
+msgstr ""
diff --git a/addons/keychain/translations/el_GR.po b/addons/keychain/translations/el_GR.po
new file mode 100644
index 000000000..4b6ce8bb7
--- /dev/null
+++ b/addons/keychain/translations/el_GR.po
@@ -0,0 +1,215 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Keychain\n"
+"Last-Translator: Overloaded\n"
+"Language-Team: none\n"
+"Language: el_GR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+msgid "OK"
+msgstr "Εντάξει"
+
+msgid "Cancel"
+msgstr "Άκυρο"
+
+msgid "Add"
+msgstr "Προσθήκη"
+
+msgid "Edit"
+msgstr "Επεξεργασία"
+
+msgid "Delete"
+msgstr "Διαγραφή"
+
+msgid "Shortcut profile:"
+msgstr "Προφίλ συντομέυσεων:"
+
+msgid "Default"
+msgstr "Προεπιλογή"
+
+msgid "Custom"
+msgstr "Προσαρμοσμένο"
+
+msgid "New"
+msgstr "Νέο"
+
+msgid "Rename"
+msgstr "Μετονομασία"
+
+msgid "Open Folder"
+msgstr "Άνοιγμα φακέλου"
+
+msgid "New Shortcut Profile"
+msgstr "Νέο προφίλ συντομέυσεων"
+
+msgid "Rename Shortcut Profile"
+msgstr "Μετονομασία προφίλ συντομεύσεων"
+
+msgid "Are you sure you want to delete this shortcut profile?"
+msgstr "Είστε σίγουροι πως θέλετε να διαγραφτεί αυτό το προφίλ συντομέυσεων;"
+
+msgid "Key"
+msgstr "Πλήκτρο"
+
+msgid "Mouse Button"
+msgstr "Κουμπί ποντικιού"
+
+msgid "Joy Button"
+msgstr "Κουμπί χειριστηρίου"
+
+msgid "Joy Axis"
+msgstr "Άξονας χειριστηρίου"
+
+msgid "Button"
+msgstr "Κουμπί"
+
+msgid "Axis"
+msgstr "Άξονας"
+
+msgid "(Physical)"
+msgstr "(Φυσικό)"
+
+msgid "Set the shortcut"
+msgstr "Επιλέξτε μια συντόμευση"
+
+msgid "Press a key or a key combination to set the shortcut"
+msgstr "Πατήστε ένα πλήκτρο ή συνδυασμό πλήκτρων για να ορίσετε τη συντόμευση"
+
+msgid "Mouse Button Index:"
+msgstr "Δείκτης κουμπιού ποντικού"
+
+msgid "Joypad Button Index:"
+msgstr "Δείκτης κουμπιού χειριστηρίου"
+
+msgid "Joypad Axis Index:"
+msgstr "Δείκτης άξονα χειριστηρίου"
+
+msgid "Already assigned to: %s"
+msgstr "Έχει ήδη εκχωρηθεί σε: %s"
+
+msgid "Left Button"
+msgstr "Αριστερό κουμπί"
+
+msgid "Right Button"
+msgstr "Δεξί κουμπί"
+
+msgid "Middle Button"
+msgstr "Μεσαίο κουμπί"
+
+msgid "Wheel Up Button"
+msgstr "Τρόχος πάνω"
+
+msgid "Wheel Down Button"
+msgstr "Τρόχος κάτω"
+
+msgid "Wheel Left Button"
+msgstr "Τρόχος αριστερά"
+
+msgid "Wheel Right Button"
+msgstr "Τρόχος δεξιά"
+
+msgid "X Button 1"
+msgstr "Κουμπί X 1"
+
+msgid "X Button 2"
+msgstr "Κουμπί X 2"
+
+msgid "DualShock Cross, Xbox A, Nintendo B"
+msgstr "DualShock Σταυρός, Xbox A, Nintendo B"
+
+msgid "DualShock Circle, Xbox B, Nintendo A"
+msgstr "DualShock Κύκλος, Xbox B, Nintendo A"
+
+msgid "DualShock Square, Xbox X, Nintendo Y"
+msgstr "DualShock Τετράγωνο, Xbox X, Nintendo Y"
+
+msgid "DualShock Triangle, Xbox Y, Nintendo X"
+msgstr "DualShock Τρίγωνο, Xbox Y, Nintendo X"
+
+msgid "L, L1"
+msgstr "L, L1"
+
+msgid "R, R1"
+msgstr "R, R1"
+
+msgid "L2"
+msgstr "L2"
+
+msgid "R2"
+msgstr "R2"
+
+msgid "L3"
+msgstr "L3"
+
+msgid "R3"
+msgstr "R3"
+
+msgid "Select, DualShock Share, Nintendo -"
+msgstr ""
+
+msgid "Start, DualShock Options, Nintendo +"
+msgstr ""
+
+msgid "D-Pad Up"
+msgstr "D-Pad Πάνω"
+
+msgid "D-Pad Down"
+msgstr "D-Pad Κάτω"
+
+msgid "D-Pad Left"
+msgstr "D-Pad Αριστερά"
+
+msgid "D-Pad Right"
+msgstr "D-Pad Δεξιά"
+
+msgid "Home, DualShock PS, Guide"
+msgstr ""
+
+msgid "Xbox Share, PS5 Microphone, Nintendo Capture"
+msgstr "Xbox Share, PS5 Μικρόφωνο, Nintendo Capture"
+
+msgid "Xbox Paddle 1"
+msgstr ""
+
+msgid "Xbox Paddle 2"
+msgstr ""
+
+msgid "Xbox Paddle 3"
+msgstr ""
+
+msgid "Xbox Paddle 4"
+msgstr ""
+
+msgid "PS4/5 Touchpad"
+msgstr "PS4/5 επιφάνεια αφής"
+
+msgid "(Left Stick Left)"
+msgstr "(Αριστερό Stick Αριστερά)"
+
+msgid "(Left Stick Right)"
+msgstr "(Αριστερό Stick Δεξιά)"
+
+msgid "(Left Stick Up)"
+msgstr "(Αριστερό Stick Πάνω)"
+
+msgid "(Left Stick Down)"
+msgstr "(Αριστερό Stick Κάτω)"
+
+msgid "(Right Stick Left)"
+msgstr "(Δεξί Stick Αριστερά)"
+
+msgid "(Right Stick Right)"
+msgstr "(Δεξί Stick Δεξιά)"
+
+msgid "(Right Stick Up)"
+msgstr "(Δεξί Stick Πάνω)"
+
+msgid "(Right Stick Down)"
+msgstr "(Δεξί Stick Κάτω)"
+
+msgid "(L2)"
+msgstr "(L2)"
+
+msgid "(R2)"
+msgstr "(R2)"
diff --git a/project.godot b/project.godot
index a3c1f5941..00f6dd206 100644
--- a/project.godot
+++ b/project.godot
@@ -109,6 +109,11 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://src/Classes/ShaderImageEffect.gd"
}, {
+"base": "Resource",
+"class": "ShortcutProfile",
+"language": "GDScript",
+"path": "res://addons/keychain/ShortcutProfile.gd"
+}, {
"base": "Guide",
"class": "SymmetryGuide",
"language": "GDScript",
@@ -135,6 +140,7 @@ _global_script_class_icons={
"Project": "",
"SelectionTool": "",
"ShaderImageEffect": "",
+"ShortcutProfile": "",
"SymmetryGuide": ""
}
@@ -168,6 +174,7 @@ Tools="*res://src/Autoload/Tools.gd"
Html5FileExchange="*res://src/Autoload/HTML5FileExchange.gd"
Export="*res://src/Autoload/Export.gd"
Palettes="*res://src/Autoload/Palettes.gd"
+Keychain="*res://addons/keychain/Keychain.gd"
[debug]
@@ -187,7 +194,7 @@ window/per_pixel_transparency/enabled.Android=false
[editor_plugins]
-enabled=PoolStringArray( "res://addons/dockable_container/plugin.cfg" )
+enabled=PoolStringArray( "res://addons/dockable_container/plugin.cfg", "res://addons/keychain/plugin.cfg" )
[gui]
@@ -310,6 +317,7 @@ right_shading_tool={
toggle_fullscreen={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777254,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
left_colorpicker_tool={
@@ -332,19 +340,15 @@ ctrl={
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-redo_secondary={
-"deadzone": 0.5,
-"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":true,"meta":false,"command":true,"pressed":false,"scancode":90,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
- ]
-}
delete={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777224,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-space={
+pan={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":32,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":3,"pressed":false,"doubleclick":false,"script":null)
]
}
new_file={
@@ -390,6 +394,7 @@ undo={
redo={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":89,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":true,"meta":false,"command":true,"pressed":false,"scancode":90,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
show_grid={
@@ -552,22 +557,17 @@ right_magic_wand_tool={
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":81,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-enter={
+confirm={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777221,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777222,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-escape={
+cancel={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777217,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-alt={
-"deadzone": 0.5,
-"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777240,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
- ]
-}
left_linetool_tool={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":76,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
@@ -613,11 +613,107 @@ new_brush={
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":66,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
-edit_mode={
+moveable_panels={
"deadzone": 0.5,
"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777252,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
]
}
+change_tool_mode={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+draw_create_line={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+draw_snap_angle={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+shape_perfect={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+shape_center={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+shape_displace={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777240,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+selection_add={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+selection_subtract={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+selection_intersect={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":true,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":16777237,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+transform_snap_axis={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777237,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+transform_snap_grid={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+transform_move_selection_only={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777240,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+transform_copy_selection_content={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":true,"meta":false,"command":true,"pressed":false,"scancode":16777240,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":true,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777238,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+draw_color_picker={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777240,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+ ]
+}
+camera_left={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777231,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":2,"axis_value":-1.0,"script":null)
+ ]
+}
+camera_right={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777233,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":2,"axis_value":1.0,"script":null)
+ ]
+}
+camera_up={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777232,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":3,"axis_value":-1.0,"script":null)
+ ]
+}
+camera_down={
+"deadzone": 0.5,
+"events": [ Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":16777234,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":3,"axis_value":1.0,"script":null)
+ ]
+}
[locale]
diff --git a/src/Autoload/Global.gd b/src/Autoload/Global.gd
index 32cf05c26..97ae9b6c0 100644
--- a/src/Autoload/Global.gd
+++ b/src/Autoload/Global.gd
@@ -8,6 +8,43 @@ enum TileMode { NONE, BOTH, X_AXIS, Y_AXIS }
enum IconColorFrom { THEME, CUSTOM }
enum ButtonSize { SMALL, BIG }
+enum FileMenu { NEW, OPEN, OPEN_LAST_PROJECT, RECENT, SAVE, SAVE_AS, EXPORT, EXPORT_AS, QUIT }
+enum EditMenu { UNDO, REDO, COPY, CUT, PASTE, DELETE, NEW_BRUSH, PREFERENCES }
+enum ViewMenu {
+ TILE_MODE,
+ GREYSCALE_VIEW,
+ MIRROR_VIEW,
+ SHOW_GRID,
+ SHOW_PIXEL_GRID,
+ SHOW_RULERS,
+ SHOW_GUIDES,
+}
+enum WindowMenu { WINDOW_OPACITY, PANELS, LAYOUTS, MOVABLE_PANELS, ZEN_MODE, FULLSCREEN_MODE }
+enum ImageMenu {
+ SCALE_IMAGE,
+ CENTRALIZE_IMAGE,
+ CROP_IMAGE,
+ RESIZE_CANVAS,
+ FLIP,
+ ROTATE,
+ INVERT_COLORS,
+ DESATURATION,
+ OUTLINE,
+ DROP_SHADOW,
+ HSV,
+ GRADIENT,
+ SHADER
+}
+enum SelectMenu { SELECT_ALL, CLEAR_SELECTION, INVERT }
+enum HelpMenu {
+ VIEW_SPLASH_SCREEN,
+ ONLINE_DOCS,
+ ISSUE_TRACKER,
+ OPEN_LOGS_FOLDER,
+ CHANGELOG,
+ ABOUT_PIXELORAMA
+}
+
var root_directory := "."
var window_title := "" setget _title_changed # Why doesn't Godot have get_window_title()?
var config_cache := ConfigFile.new()
@@ -165,6 +202,8 @@ onready var current_version: String = ProjectSettings.get_setting("application/c
func _ready() -> void:
+ _initialize_keychain()
+
if OS.has_feature("standalone"):
root_directory = OS.get_executable_path().get_base_dir()
# root_directory must be set earlier than this is because XDGDataDirs depends on it
@@ -189,6 +228,117 @@ func _ready() -> void:
ui_tooltips[node] = tooltip
+func _initialize_keychain() -> void:
+ Keychain.config_file = config_cache
+ Keychain.actions = {
+ "new_file": Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.NEW),
+ "open_file": Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.OPEN),
+ "save_file": Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.SAVE),
+ "save_file_as":
+ Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.SAVE_AS),
+ "export_file":
+ Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.EXPORT),
+ "export_file_as":
+ Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.EXPORT_AS),
+ "quit": Keychain.MenuInputAction.new("", "File menu", true, "FileMenu", FileMenu.QUIT),
+ "redo":
+ Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.REDO, true),
+ "undo":
+ Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.UNDO, true),
+ "cut": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.CUT),
+ "copy": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.COPY),
+ "paste": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.PASTE),
+ "delete": Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.DELETE),
+ "new_brush":
+ Keychain.MenuInputAction.new("", "Edit menu", true, "EditMenu", EditMenu.NEW_BRUSH),
+ "mirror_view":
+ Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.MIRROR_VIEW),
+ "show_grid":
+ Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.SHOW_GRID),
+ "show_pixel_grid":
+ Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.SHOW_PIXEL_GRID),
+ "show_guides":
+ Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.SHOW_GUIDES),
+ "show_rulers":
+ Keychain.MenuInputAction.new("", "View menu", true, "ViewMenu", ViewMenu.SHOW_RULERS),
+ "moveable_panels":
+ Keychain.MenuInputAction.new(
+ "", "Window menu", true, "WindowMenu", WindowMenu.MOVABLE_PANELS
+ ),
+ "zen_mode":
+ Keychain.MenuInputAction.new("", "Window menu", true, "WindowMenu", WindowMenu.ZEN_MODE),
+ "toggle_fullscreen":
+ Keychain.MenuInputAction.new(
+ "", "Window menu", true, "WindowMenu", WindowMenu.FULLSCREEN_MODE
+ ),
+ "clear_selection":
+ Keychain.MenuInputAction.new(
+ "", "Select menu", true, "SelectMenu", SelectMenu.CLEAR_SELECTION
+ ),
+ "select_all":
+ Keychain.MenuInputAction.new("", "Select menu", true, "SelectMenu", SelectMenu.SELECT_ALL),
+ "invert_selection":
+ Keychain.MenuInputAction.new("", "Select menu", true, "SelectMenu", SelectMenu.INVERT),
+ "open_docs":
+ Keychain.MenuInputAction.new("", "Help menu", true, "HelpMenu", HelpMenu.ONLINE_DOCS),
+ "zoom_in": Keychain.InputAction.new("", "General"),
+ "zoom_out": Keychain.InputAction.new("", "General"),
+ "camera_left": Keychain.InputAction.new("", "General"),
+ "camera_right": Keychain.InputAction.new("", "General"),
+ "camera_up": Keychain.InputAction.new("", "General"),
+ "camera_down": Keychain.InputAction.new("", "General"),
+ "pan": Keychain.InputAction.new("", "General"),
+ "confirm": Keychain.InputAction.new("", "General"),
+ "cancel": Keychain.InputAction.new("", "General"),
+ "switch_colors": Keychain.InputAction.new("", "Buttons"),
+ "go_to_first_frame": Keychain.InputAction.new("", "Buttons"),
+ "go_to_last_frame": Keychain.InputAction.new("", "Buttons"),
+ "go_to_previous_frame": Keychain.InputAction.new("", "Buttons"),
+ "go_to_next_frame": Keychain.InputAction.new("", "Buttons"),
+ "play_backwards": Keychain.InputAction.new("", "Buttons"),
+ "play_forward": Keychain.InputAction.new("", "Buttons"),
+ "change_tool_mode": Keychain.InputAction.new("", "Tool modifiers", false),
+ "draw_create_line": Keychain.InputAction.new("", "Draw tools", false),
+ "draw_snap_angle": Keychain.InputAction.new("", "Draw tools", false),
+ "draw_color_picker": Keychain.InputAction.new("", "Draw tools", false),
+ "shape_perfect": Keychain.InputAction.new("", "Shape tools", false),
+ "shape_center": Keychain.InputAction.new("", "Shape tools", false),
+ "shape_displace": Keychain.InputAction.new("", "Shape tools", false),
+ "selection_add": Keychain.InputAction.new("", "Selection tools", false),
+ "selection_subtract": Keychain.InputAction.new("", "Selection tools", false),
+ "selection_intersect": Keychain.InputAction.new("", "Selection tools", false),
+ "transform_snap_axis": Keychain.InputAction.new("", "Transformation tools", false),
+ "transform_snap_grid": Keychain.InputAction.new("", "Transformation tools", false),
+ "transform_move_selection_only":
+ Keychain.InputAction.new("", "Transformation tools", false),
+ "transform_copy_selection_content":
+ Keychain.InputAction.new("", "Transformation tools", false),
+ }
+
+ Keychain.groups = {
+ "General": Keychain.InputGroup.new("", false),
+ "Buttons": Keychain.InputGroup.new(),
+ "Tools": Keychain.InputGroup.new(),
+ "Left": Keychain.InputGroup.new("Tools"),
+ "Right": Keychain.InputGroup.new("Tools"),
+ "Menu": Keychain.InputGroup.new(),
+ "File menu": Keychain.InputGroup.new("Menu"),
+ "Edit menu": Keychain.InputGroup.new("Menu"),
+ "View menu": Keychain.InputGroup.new("Menu"),
+ "Select menu": Keychain.InputGroup.new("Menu"),
+ "Image menu": Keychain.InputGroup.new("Menu"),
+ "Window menu": Keychain.InputGroup.new("Menu"),
+ "Help menu": Keychain.InputGroup.new("Menu"),
+ "Tool modifiers": Keychain.InputGroup.new(),
+ "Draw tools": Keychain.InputGroup.new("Tool modifiers"),
+ "Shape tools": Keychain.InputGroup.new("Tool modifiers"),
+ "Selection tools": Keychain.InputGroup.new("Tool modifiers"),
+ "Transformation tools": Keychain.InputGroup.new("Tool modifiers"),
+ }
+ Keychain.ignore_actions = ["left_mouse", "right_mouse", "middle_mouse", "shift", "ctrl"]
+ Keychain.multiple_menu_accelerators = true
+
+
func notification_label(text: String) -> void:
var notification: Label = notification_label_node.instance()
notification.text = tr(text)
@@ -320,7 +470,15 @@ func change_button_texturerect(texture_button: TextureRect, new_file_name: Strin
func update_hint_tooltips() -> void:
+ yield(get_tree(), "idle_frame")
Tools.update_hint_tooltips()
for tip in ui_tooltips:
- tip.hint_tooltip = tr(ui_tooltips[tip]) % tip.shortcut.get_as_text()
+ var hint := "None"
+ var event_type: InputEvent = tip.shortcut.shortcut
+ if event_type is InputEventKey:
+ hint = event_type.as_text()
+ elif event_type is InputEventAction:
+ var first_key: InputEventKey = Keychain.action_get_first_key(event_type.action)
+ hint = first_key.as_text() if first_key else "None"
+ tip.hint_tooltip = tr(ui_tooltips[tip]) % hint
diff --git a/src/Autoload/Tools.gd b/src/Autoload/Tools.gd
index a4079c3ff..78a94463d 100644
--- a/src/Autoload/Tools.gd
+++ b/src/Autoload/Tools.gd
@@ -6,9 +6,6 @@ var pen_pressure := 1.0
var horizontal_mirror := false
var vertical_mirror := false
var pixel_perfect := false
-var control := false
-var shift := false
-var alt := false
var tools := {
"RectSelect":
@@ -95,7 +92,7 @@ var tools := {
"""Hold %s to snap the angle of the line
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
- ["Shift", "configurable_ctrl", "configurable_alt"]
+ ["Shift", "Ctrl", "Alt"]
),
"RectangleTool":
Tool.new(
@@ -106,7 +103,7 @@ Hold %s to displace the shape's origin""",
"""Hold %s to create a 1:1 shape
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
- ["configurable_shift", "configurable_ctrl", "configurable_alt"]
+ ["Shift", "Ctrl", "Alt"]
),
"EllipseTool":
Tool.new(
@@ -117,7 +114,7 @@ Hold %s to displace the shape's origin""",
"""Hold %s to create a 1:1 shape
Hold %s to center the shape on the click origin
Hold %s to displace the shape's origin""",
- ["configurable_shift", "configurable_ctrl", "configurable_alt"]
+ ["Shift", "Ctrl", "Alt"]
),
}
@@ -138,7 +135,6 @@ class Tool:
var shortcut := ""
var extra_hint := ""
var extra_shortcuts := [] # Array of String(s)
- var extra_shortcuts_order := [] # Array to keep shift, ctrl, alt in order
var button_node: BaseButton
func _init(
@@ -155,7 +151,6 @@ class Tool:
scene = _scene
extra_hint = _extra_hint
extra_shortcuts = _extra_shortucts
- extra_shortcuts_order = _extra_shortucts.duplicate()
icon = load("res://assets/graphics/tools/%s.png" % name.to_lower())
cursor_icon = load("res://assets/graphics/tools/cursors/%s.png" % name.to_lower())
@@ -165,13 +160,17 @@ class Tool:
var left_text := ""
var right_text := ""
if InputMap.has_action("left_" + shortcut + "_tool"):
- var left_shortcut: String = InputMap.get_action_list("left_" + shortcut + "_tool")[0].as_text()
- shortcuts.append(left_shortcut)
- left_text = "\n%s for left mouse button"
+ var left_list := InputMap.get_action_list("left_" + shortcut + "_tool")
+ if left_list.size() > 0:
+ var left_shortcut: String = left_list[0].as_text()
+ shortcuts.append(left_shortcut)
+ left_text = "\n%s for left mouse button"
if InputMap.has_action("right_" + shortcut + "_tool"):
- var right_shortcut: String = InputMap.get_action_list("right_" + shortcut + "_tool")[0].as_text()
- shortcuts.append(right_shortcut)
- right_text = "\n%s for right mouse button"
+ var right_list := InputMap.get_action_list("right_" + shortcut + "_tool")
+ if right_list.size() > 0:
+ var right_shortcut: String = right_list[0].as_text()
+ shortcuts.append(right_shortcut)
+ right_text = "\n%s for right mouse button"
if !shortcuts.empty():
hint += "\n" + left_text + right_text
@@ -179,22 +178,6 @@ class Tool:
if !extra_hint.empty():
hint += "\n\n" + extra_hint
- # Some tools have shift,ctrl,alt "HARD CODED" in them using (InputEventWithModifiers)
- # But Others only use the regular is_action_pressed() function
- # their Shift, Ctrl, Alt are listed Below
- var code_shift = InputMap.get_action_list("shift")[0].get_scancode_with_modifiers()
- var code_ctrl = InputMap.get_action_list("ctrl")[0].get_scancode_with_modifiers()
- var code_alt = InputMap.get_action_list("alt")[0].get_scancode_with_modifiers()
- var configurable_shift: String = OS.get_scancode_string(code_shift)
- var configurable_ctrl: String = OS.get_scancode_string(code_ctrl)
- var configurable_alt: String = OS.get_scancode_string(code_alt)
- for shortcut_idx in extra_shortcuts.size():
- if extra_shortcuts_order[shortcut_idx] == "configurable_shift":
- extra_shortcuts[shortcut_idx] = configurable_shift
- if extra_shortcuts_order[shortcut_idx] == "configurable_ctrl":
- extra_shortcuts[shortcut_idx] = configurable_ctrl
- if extra_shortcuts_order[shortcut_idx] == "configurable_alt":
- extra_shortcuts[shortcut_idx] = configurable_alt
shortcuts.append_array(extra_shortcuts)
if shortcuts.empty():
@@ -220,6 +203,12 @@ func _ready() -> void:
_tool_buttons = Global.control.find_node("ToolButtons")
for t in tools:
add_tool_button(tools[t])
+ var tool_shortcut: String = Tools.tools[t].shortcut
+ var left_tool_shortcut := "left_%s_tool" % tool_shortcut
+ var right_tool_shortcut := "right_%s_tool" % tool_shortcut
+ Keychain.actions[left_tool_shortcut] = Keychain.InputAction.new("", "Left")
+ Keychain.actions[right_tool_shortcut] = Keychain.InputAction.new("", "Right")
+
_slots[BUTTON_LEFT] = Slot.new("Left tool")
_slots[BUTTON_RIGHT] = Slot.new("Right tool")
_panels[BUTTON_LEFT] = Global.control.find_node("LeftPanelContainer", true, false)
@@ -383,11 +372,6 @@ func handle_draw(position: Vector2, event: InputEvent) -> void:
if Global.mirror_view:
draw_pos.x = Global.current_project.size.x - position.x - 1
- if event is InputEventWithModifiers:
- control = event.control
- shift = event.shift
- alt = event.alt
-
if event is InputEventMouseButton:
if event.button_index in [BUTTON_LEFT, BUTTON_RIGHT]:
if event.pressed and _active_button == -1:
diff --git a/src/Main.gd b/src/Main.gd
index f55ffa551..65bc4f0ac 100644
--- a/src/Main.gd
+++ b/src/Main.gd
@@ -84,26 +84,6 @@ func _input(event: InputEvent) -> void:
if get_focus_owner() is LineEdit:
get_focus_owner().release_focus()
- # The section of code below is reserved for Undo and Redo!
- # Do not place code for Input below, but above.
- if !event.is_echo(): # Checks if the action is pressed down
- if event.is_action_pressed("redo_secondary"):
- # Done, so that "redo_secondary" hasn't a slight delay before it starts.
- # The "redo" and "undo" action don't have a slight delay,
- # because they get called as an accelerator once pressed (TopMenuContainer.gd, Line 152)
- Global.current_project.commit_redo()
- return
-
- if event.is_action("redo"): # Ctrl + Y
- Global.current_project.commit_redo()
-
- if event.is_action("redo_secondary"): # Shift + Ctrl + Z
- Global.current_project.commit_redo()
-
- if event.is_action("undo") and !event.shift: # Ctrl + Z and check if shift isn't pressed
- # so "undo" isn't accidentaly triggered while using "redo_secondary"
- Global.current_project.commit_undo()
-
func _setup_application_window_size() -> void:
get_tree().set_screen_stretch(
diff --git a/src/Main.tscn b/src/Main.tscn
index 1e86da4c5..c9fac5c12 100644
--- a/src/Main.tscn
+++ b/src/Main.tscn
@@ -26,7 +26,6 @@ theme = ExtResource( 1 )
script = ExtResource( 2 )
__meta__ = {
"_edit_horizontal_guides_": [ ],
-"_edit_use_anchors_": false,
"_edit_vertical_guides_": [ ]
}
diff --git a/src/Preferences/HandleShortcuts.gd b/src/Preferences/HandleShortcuts.gd
deleted file mode 100644
index 14c3ded47..000000000
--- a/src/Preferences/HandleShortcuts.gd
+++ /dev/null
@@ -1,240 +0,0 @@
-extends Node
-
-var default_shortcuts_preset := {}
-var custom_shortcuts_preset := {}
-var action_being_edited := ""
-var shortcut_already_assigned = false
-var old_input_event: InputEventKey
-var new_input_event: InputEventKey
-
-onready var shortcut_selector_popup = Global.preferences_dialog.get_node("Popups/ShortcutSelector")
-onready var theme_font_color: Color = shortcut_selector_popup.get_node("EnteredShortcut").get_color(
- "font_color"
-)
-
-
-func _ready() -> void:
- # Disable input until the shortcut selector is displayed
- set_process_input(false)
-
- # Get default preset for shortcuts from project input map
- # Buttons in shortcuts selector should be called the same as actions
- for shortcut_grid_item in get_node("Shortcuts").get_children():
- if shortcut_grid_item is Button:
- var input_events = InputMap.get_action_list(shortcut_grid_item.name)
- if input_events.size() > 1:
- printerr(
- "Every shortcut action should have just one input event assigned in input map"
- )
- shortcut_grid_item.text = (input_events[0] as InputEventKey).as_text()
- shortcut_grid_item.connect(
- "pressed", self, "_on_Shortcut_button_pressed", [shortcut_grid_item]
- )
- default_shortcuts_preset[shortcut_grid_item.name] = input_events[0]
-
- # Load custom shortcuts from the config file
- custom_shortcuts_preset = default_shortcuts_preset.duplicate()
- for action in default_shortcuts_preset:
- var saved_input_event = Global.config_cache.get_value("shortcuts", action, 0)
- if saved_input_event is InputEventKey:
- custom_shortcuts_preset[action] = saved_input_event
-
- var shortcuts_preset = Global.config_cache.get_value("shortcuts", "shortcuts_preset", 0)
- get_node("HBoxContainer/PresetOptionButton").select(shortcuts_preset)
- _on_PresetOptionButton_item_selected(shortcuts_preset)
-
-
-func _input(event: InputEvent) -> void:
- if event is InputEventKey:
- if event.pressed:
- if event.scancode == KEY_ESCAPE:
- shortcut_selector_popup.hide()
- else:
- # Check if shortcut was already used
- for action in InputMap.get_actions():
- for input_event in InputMap.get_action_list(action):
- if input_event is InputEventKey:
- if (
- OS.get_scancode_string(input_event.get_scancode_with_modifiers())
- == OS.get_scancode_string(event.get_scancode_with_modifiers())
- ):
- shortcut_selector_popup.get_node("EnteredShortcut").text = tr(
- "Already assigned"
- )
- shortcut_selector_popup.get_node("EnteredShortcut").add_color_override(
- "font_color", Color.crimson
- )
- get_tree().set_input_as_handled()
- shortcut_already_assigned = true
- return
-
- # Store new shortcut
- shortcut_already_assigned = false
- old_input_event = InputMap.get_action_list(action_being_edited)[0]
- new_input_event = event
- shortcut_selector_popup.get_node("EnteredShortcut").text = OS.get_scancode_string(
- event.get_scancode_with_modifiers()
- )
- shortcut_selector_popup.get_node("EnteredShortcut").add_color_override(
- "font_color", theme_font_color
- )
- get_tree().set_input_as_handled()
-
-
-func _on_PresetOptionButton_item_selected(id: int) -> void:
- # Only custom preset which is modifiable
- toggle_shortcut_buttons(true if id == 1 else false)
- match id:
- 0:
- apply_shortcuts_preset(default_shortcuts_preset)
- 1:
- apply_shortcuts_preset(custom_shortcuts_preset)
- Global.config_cache.set_value("shortcuts", "shortcuts_preset", id)
- Global.config_cache.save("user://cache.ini")
-
-
-func apply_shortcuts_preset(preset) -> void:
- for action in preset:
- var preset_old_input_event: InputEventKey = InputMap.get_action_list(action)[0]
- set_action_shortcut(action, preset_old_input_event, preset[action])
- get_node("Shortcuts/" + action).text = OS.get_scancode_string(
- preset[action].get_scancode_with_modifiers()
- )
- Global.update_hint_tooltips()
-
-
-func toggle_shortcut_buttons(enabled: bool) -> void:
- for shortcut_grid_item in get_node("Shortcuts").get_children():
- if shortcut_grid_item is Button:
- shortcut_grid_item.disabled = not enabled
- if shortcut_grid_item.disabled:
- shortcut_grid_item.mouse_default_cursor_shape = Control.CURSOR_FORBIDDEN
- else:
- shortcut_grid_item.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
-
-
-func set_action_shortcut(action: String, oldinput: InputEventKey, newinput: InputEventKey) -> void:
- # Only updates the InputMap
- InputMap.action_erase_event(action, oldinput)
- InputMap.action_add_event(action, newinput)
- update_ui_shortcuts(action)
-
-
-func update_ui_shortcuts(action: String):
- # Updates UI elements according to InputMap (Tool shortcuts are updated from a seperate function)
- # Set shortcut to switch colors button
- if action == "switch_colors":
- update_ui_shortcut("ColorSwitch", action)
- # Set timeline shortcuts
- if action == "go_to_first_frame":
- update_ui_shortcut("FirstFrame", action)
- if action == "go_to_previous_frame":
- update_ui_shortcut("PreviousFrame", action)
- if action == "play_backwards":
- update_ui_shortcut("PlayBackwards", action)
- if action == "play_forward":
- update_ui_shortcut("PlayForward", action)
- if action == "go_to_next_frame":
- update_ui_shortcut("NextFrame", action)
- if action == "go_to_last_frame":
- update_ui_shortcut("LastFrame", action)
- # Set shortcuts for Menu Options
- var top_menu: Panel = Global.control.find_node("TopMenuContainer")
- var file_menu: PopupMenu = top_menu.file_menu_button.get_popup()
- var edit_menu: PopupMenu = top_menu.edit_menu_button.get_popup()
- var select_menu: PopupMenu = top_menu.select_menu_button.get_popup()
- var view_menu: PopupMenu = top_menu.view_menu_button.get_popup()
- var window_menu: PopupMenu = top_menu.window_menu_button.get_popup()
- var help_menu: PopupMenu = top_menu.help_menu_button.get_popup()
- if action == "new_file":
- update_menu_option(file_menu, "New...", action)
- if action == "open_file":
- update_menu_option(file_menu, "Open...", action)
- if action == "save_file":
- update_menu_option(file_menu, "Save...", action)
- if action == "save_file_as":
- update_menu_option(file_menu, "Save as...", action)
- if action == "export_file":
- update_menu_option(file_menu, "Export...", action)
- if action == "export_file_as":
- update_menu_option(file_menu, "Export as...", action)
- if action == "quit":
- update_menu_option(file_menu, "Quit", action)
- if action == "undo":
- update_menu_option(edit_menu, "Undo", action)
- if action == "redo":
- update_menu_option(edit_menu, "Redo", action)
- if action == "copy":
- update_menu_option(edit_menu, "Copy", action)
- if action == "cut":
- update_menu_option(edit_menu, "Cut", action)
- if action == "paste":
- update_menu_option(edit_menu, "Paste", action)
- if action == "delete":
- update_menu_option(edit_menu, "Delete", action)
- if action == "new_brush":
- update_menu_option(edit_menu, "New Brush", action)
- if action == "select_all":
- update_menu_option(select_menu, "All", action)
- if action == "clear_selection":
- update_menu_option(select_menu, "Clear", action)
- if action == "invert_selection":
- update_menu_option(select_menu, "Invert", action)
- if action == "mirror_view":
- update_menu_option(view_menu, "Mirror View", action)
- if action == "show_grid":
- update_menu_option(view_menu, "Show Grid", action)
- if action == "show_pixel_grid":
- update_menu_option(view_menu, "Show Pixel Grid", action)
- if action == "show_rulers":
- update_menu_option(view_menu, "Show Rulers", action)
- if action == "show_guides":
- update_menu_option(view_menu, "Show Guides", action)
- if action == "edit_mode":
- for child in window_menu.get_children():
- if child.name == "panels_submenu":
- update_menu_option(child, "Moveable Panels", action)
- if action == "zen_mode":
- update_menu_option(window_menu, "Zen Mode", action)
- if action == "toggle_fullscreen":
- update_menu_option(window_menu, "Fullscreen Mode", action)
- if action == "open_docs":
- update_menu_option(help_menu, "Online Docs", action)
-
-
-func update_ui_shortcut(name: String, action: String):
- var ui_button: BaseButton = Global.control.find_node(name)
- ui_button.shortcut.shortcut = InputMap.get_action_list(action)[0]
-
-
-func update_menu_option(menu: PopupMenu, name: String, action: String):
- for idx in menu.get_item_count():
- if menu.get_item_text(idx) == name:
- var accel: int = InputMap.get_action_list(action)[0].get_scancode_with_modifiers()
- menu.set_item_accelerator(idx, accel)
-
-
-func _on_Shortcut_button_pressed(button: Button) -> void:
- set_process_input(true)
- action_being_edited = button.name
- new_input_event = InputMap.get_action_list(button.name)[0]
- shortcut_already_assigned = true
- shortcut_selector_popup.popup_centered()
-
-
-func _on_ShortcutSelector_popup_hide() -> void:
- set_process_input(false)
- shortcut_selector_popup.get_node("EnteredShortcut").text = ""
-
-
-func _on_ShortcutSelector_confirmed() -> void:
- if not shortcut_already_assigned:
- set_action_shortcut(action_being_edited, old_input_event, new_input_event)
- Global.update_hint_tooltips()
- custom_shortcuts_preset[action_being_edited] = new_input_event
- Global.config_cache.set_value("shortcuts", action_being_edited, new_input_event)
- Global.config_cache.save("user://cache.ini")
- get_node("Shortcuts/" + action_being_edited).text = OS.get_scancode_string(
- new_input_event.get_scancode_with_modifiers()
- )
- shortcut_selector_popup.hide()
diff --git a/src/Preferences/PreferencesDialog.gd b/src/Preferences/PreferencesDialog.gd
index b4bc428c9..b2b201806 100644
--- a/src/Preferences/PreferencesDialog.gd
+++ b/src/Preferences/PreferencesDialog.gd
@@ -87,6 +87,7 @@ onready var autosave_container: Container = right_side.get_node("Backup/Autosave
onready var autosave_interval: SpinBox = autosave_container.get_node("AutosaveInterval")
onready var shrink_label: Label = right_side.get_node("Interface/ShrinkContainer/ShrinkLabel")
onready var themes: BoxContainer = right_side.get_node("Interface/Themes")
+onready var shortcuts: Control = right_side.get_node("Shortcuts")
onready var extensions: BoxContainer = right_side.get_node("Extensions")
@@ -106,6 +107,12 @@ class Preference:
func _ready() -> void:
# Replace OK since preference changes are being applied immediately, not after OK confirmation
get_ok().text = tr("Close")
+
+ for child in shortcuts.get_children():
+ if not child is AcceptDialog:
+ continue
+ child.connect("confirmed", Global, "update_hint_tooltips")
+
for child in right_side.get_children():
content_list.append(child.name)
diff --git a/src/Preferences/PreferencesDialog.tscn b/src/Preferences/PreferencesDialog.tscn
index 5036da014..f68f0bb5a 100644
--- a/src/Preferences/PreferencesDialog.tscn
+++ b/src/Preferences/PreferencesDialog.tscn
@@ -2,9 +2,9 @@
[ext_resource path="res://src/Preferences/PreferencesDialog.gd" type="Script" id=1]
[ext_resource path="res://src/Preferences/HandleExtensions.gd" type="Script" id=2]
+[ext_resource path="res://addons/keychain/ShortcutEdit.tscn" type="PackedScene" id=3]
[ext_resource path="res://src/Preferences/HandleLanguages.gd" type="Script" id=4]
[ext_resource path="res://src/Preferences/HandleThemes.gd" type="Script" id=5]
-[ext_resource path="res://src/Preferences/HandleShortcuts.gd" type="Script" id=6]
[sub_resource type="ButtonGroup" id=1]
@@ -52,8 +52,9 @@ size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="HSplitContainer/ScrollContainer"]
margin_right = 498.0
-margin_bottom = 52.0
+margin_bottom = 406.0
size_flags_horizontal = 3
+size_flags_vertical = 3
[node name="Startup" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
margin_right = 498.0
@@ -819,1193 +820,11 @@ margin_bottom = 48.0
rect_min_size = Vector2( 64, 20 )
mouse_default_cursor_shape = 2
-[node name="Shortcuts" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
+[node name="Shortcuts" parent="HSplitContainer/ScrollContainer/VBoxContainer" instance=ExtResource( 3 )]
visible = false
margin_top = 56.0
-margin_right = 486.0
-margin_bottom = 1268.0
-script = ExtResource( 6 )
-
-[node name="HBoxContainer" type="HBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
-margin_right = 486.0
-margin_bottom = 20.0
-hint_tooltip = "Only custom preset can be modified"
-
-[node name="Label" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer"]
-margin_top = 3.0
-margin_right = 45.0
-margin_bottom = 17.0
-text = "Preset:"
-
-[node name="PresetOptionButton" type="OptionButton" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer"]
-margin_left = 49.0
-margin_right = 486.0
-margin_bottom = 20.0
-hint_tooltip = "Only custom preset can be modified"
-mouse_default_cursor_shape = 2
-size_flags_horizontal = 3
-text = "Default"
-items = [ "Default", null, false, 0, null, "Custom", null, false, 1, null ]
-selected = 0
-
-[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
-margin_top = 24.0
-margin_right = 486.0
-margin_bottom = 28.0
-
-[node name="Shortcuts" type="GridContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts"]
-margin_top = 32.0
-margin_right = 486.0
-margin_bottom = 1212.0
-custom_constants/vseparation = 2
-custom_constants/hseparation = 5
-columns = 3
-__meta__ = {
-"_editor_description_": ""
-}
-
-[node name="TitleTool" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_right = 151.0
-margin_bottom = 14.0
-text = "Tools:"
-
-[node name="Empty" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_right = 318.0
-margin_bottom = 14.0
-
-[node name="Empty2" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_right = 485.0
-margin_bottom = 14.0
-
-[node name="Empty3" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 16.0
-margin_right = 151.0
-margin_bottom = 30.0
-
-[node name="LeftToolLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 16.0
-margin_right = 318.0
-margin_bottom = 30.0
-hint_tooltip = "A tool assigned to the left mouse button"
-mouse_filter = 0
-text = "Left Tool:"
-align = 1
-
-[node name="RightToolLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 16.0
-margin_right = 485.0
-margin_bottom = 30.0
-hint_tooltip = "A tool assigned to the right mouse button"
-mouse_filter = 0
-text = "Right Tool:"
-align = 1
-
-[node name="Empty4" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 32.0
-margin_right = 151.0
-margin_bottom = 36.0
-
-[node name="HSeparator" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-visible = false
-margin_left = 167.0
-margin_top = 16.0
-margin_right = 318.0
-margin_bottom = 20.0
-
-[node name="HSeparator2" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 32.0
-margin_right = 318.0
-margin_bottom = 36.0
-
-[node name="HSeparator3" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 32.0
-margin_right = 485.0
-margin_bottom = 36.0
-
-[node name="RectSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 41.0
-margin_right = 151.0
-margin_bottom = 55.0
-text = "Rectangular Selection"
-
-[node name="left_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 38.0
-margin_right = 318.0
-margin_bottom = 58.0
-size_flags_horizontal = 3
-
-[node name="right_rectangle_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 38.0
-margin_right = 485.0
-margin_bottom = 58.0
-size_flags_horizontal = 3
-
-[node name="EllipseSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 63.0
-margin_right = 151.0
-margin_bottom = 77.0
-text = "Elliptical Selection"
-
-[node name="left_ellipse_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 60.0
-margin_right = 318.0
-margin_bottom = 80.0
-size_flags_horizontal = 3
-
-[node name="right_ellipse_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 60.0
-margin_right = 485.0
-margin_bottom = 80.0
-size_flags_horizontal = 3
-
-[node name="PolygonalSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 85.0
-margin_right = 151.0
-margin_bottom = 99.0
-text = "Polygonal Selection"
-
-[node name="left_polygon_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 82.0
-margin_right = 318.0
-margin_bottom = 102.0
-size_flags_horizontal = 3
-
-[node name="right_polygon_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 82.0
-margin_right = 485.0
-margin_bottom = 102.0
-size_flags_horizontal = 3
-
-[node name="ColorSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 107.0
-margin_right = 151.0
-margin_bottom = 121.0
-text = "Select By Color"
-
-[node name="left_color_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 104.0
-margin_right = 318.0
-margin_bottom = 124.0
-size_flags_horizontal = 3
-
-[node name="right_color_select_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 104.0
-margin_right = 485.0
-margin_bottom = 124.0
-size_flags_horizontal = 3
-
-[node name="MagicWandLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 129.0
-margin_right = 151.0
-margin_bottom = 143.0
-text = "Magic Wand"
-
-[node name="left_magic_wand_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 126.0
-margin_right = 318.0
-margin_bottom = 146.0
-size_flags_horizontal = 3
-
-[node name="right_magic_wand_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 126.0
-margin_right = 485.0
-margin_bottom = 146.0
-size_flags_horizontal = 3
-
-[node name="LassoSelectLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 151.0
-margin_right = 151.0
-margin_bottom = 165.0
-text = "Lasso / Free Select Tool"
-
-[node name="left_lasso_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 148.0
-margin_right = 318.0
-margin_bottom = 168.0
-size_flags_horizontal = 3
-
-[node name="right_lasso_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 148.0
-margin_right = 485.0
-margin_bottom = 168.0
-size_flags_horizontal = 3
-
-[node name="MoveLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 173.0
-margin_right = 151.0
-margin_bottom = 187.0
-text = "Move"
-
-[node name="left_move_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 170.0
-margin_right = 318.0
-margin_bottom = 190.0
-size_flags_horizontal = 3
-
-[node name="right_move_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 170.0
-margin_right = 485.0
-margin_bottom = 190.0
-size_flags_horizontal = 3
-
-[node name="ZoomLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 195.0
-margin_right = 151.0
-margin_bottom = 209.0
-text = "Zoom"
-
-[node name="left_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 192.0
-margin_right = 318.0
-margin_bottom = 212.0
-size_flags_horizontal = 3
-
-[node name="right_zoom_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 192.0
-margin_right = 485.0
-margin_bottom = 212.0
-size_flags_horizontal = 3
-
-[node name="PanLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 217.0
-margin_right = 151.0
-margin_bottom = 231.0
-text = "Pan"
-
-[node name="left_pan_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 214.0
-margin_right = 318.0
-margin_bottom = 234.0
-size_flags_horizontal = 3
-
-[node name="right_pan_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 214.0
-margin_right = 485.0
-margin_bottom = 234.0
-size_flags_horizontal = 3
-
-[node name="ColorPickerLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 239.0
-margin_right = 151.0
-margin_bottom = 253.0
-text = "Color Picker"
-
-[node name="left_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 236.0
-margin_right = 318.0
-margin_bottom = 256.0
-
-[node name="right_colorpicker_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 236.0
-margin_right = 485.0
-margin_bottom = 256.0
-
-[node name="PencilLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 261.0
-margin_right = 151.0
-margin_bottom = 275.0
-text = "Pencil"
-
-[node name="left_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 258.0
-margin_right = 318.0
-margin_bottom = 278.0
-
-[node name="right_pencil_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 258.0
-margin_right = 485.0
-margin_bottom = 278.0
-
-[node name="EraserLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 283.0
-margin_right = 151.0
-margin_bottom = 297.0
-text = "Eraser"
-
-[node name="left_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 280.0
-margin_right = 318.0
-margin_bottom = 300.0
-
-[node name="right_eraser_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 280.0
-margin_right = 485.0
-margin_bottom = 300.0
-
-[node name="BucketLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 305.0
-margin_right = 151.0
-margin_bottom = 319.0
-text = "Bucket"
-
-[node name="left_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 302.0
-margin_right = 318.0
-margin_bottom = 322.0
-
-[node name="right_fill_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 302.0
-margin_right = 485.0
-margin_bottom = 322.0
-
-[node name="ShadingLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 327.0
-margin_right = 151.0
-margin_bottom = 341.0
-text = "Shading Tool"
-
-[node name="left_shading_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 324.0
-margin_right = 318.0
-margin_bottom = 344.0
-
-[node name="right_shading_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 324.0
-margin_right = 485.0
-margin_bottom = 344.0
-
-[node name="LineLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 349.0
-margin_right = 151.0
-margin_bottom = 363.0
-text = "Line Tool"
-
-[node name="left_linetool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 346.0
-margin_right = 318.0
-margin_bottom = 366.0
-
-[node name="right_linetool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 346.0
-margin_right = 485.0
-margin_bottom = 366.0
-
-[node name="RectangleLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 371.0
-margin_right = 151.0
-margin_bottom = 385.0
-text = "Rectangle Tool"
-
-[node name="left_rectangletool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 368.0
-margin_right = 318.0
-margin_bottom = 388.0
-
-[node name="right_rectangletool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 368.0
-margin_right = 485.0
-margin_bottom = 388.0
-
-[node name="EllipseLabel" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 393.0
-margin_right = 151.0
-margin_bottom = 407.0
-text = "Ellipse Tool"
-
-[node name="left_ellipsetool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 390.0
-margin_right = 318.0
-margin_bottom = 410.0
-
-[node name="right_ellipsetool_tool" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 390.0
-margin_right = 485.0
-margin_bottom = 410.0
-
-[node name="HSeparator4" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 412.0
-margin_right = 151.0
-margin_bottom = 416.0
-
-[node name="HSeparator5" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 412.0
-margin_right = 318.0
-margin_bottom = 416.0
-
-[node name="HSeparator6" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 412.0
-margin_right = 485.0
-margin_bottom = 416.0
-
-[node name="TitleUI" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 418.0
-margin_right = 151.0
-margin_bottom = 432.0
-text = "UI Elements:"
-
-[node name="Empty5" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 418.0
-margin_right = 318.0
-margin_bottom = 432.0
-
-[node name="Empty6" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 418.0
-margin_right = 485.0
-margin_bottom = 432.0
-
-[node name="Switch Colors" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 437.0
-margin_right = 151.0
-margin_bottom = 451.0
-text = "Switch Colors"
-
-[node name="switch_colors" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 434.0
-margin_right = 318.0
-margin_bottom = 454.0
-
-[node name="Empty7" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 434.0
-margin_right = 485.0
-margin_bottom = 454.0
-
-[node name="First Frame" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 459.0
-margin_right = 151.0
-margin_bottom = 473.0
-text = "First Frame"
-
-[node name="go_to_first_frame" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 456.0
-margin_right = 318.0
-margin_bottom = 476.0
-
-[node name="Empty8" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 456.0
-margin_right = 485.0
-margin_bottom = 476.0
-
-[node name="Previous Frame" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 481.0
-margin_right = 151.0
-margin_bottom = 495.0
-text = "Previous Frame"
-
-[node name="go_to_previous_frame" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 478.0
-margin_right = 318.0
-margin_bottom = 498.0
-
-[node name="Empty9" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 478.0
-margin_right = 485.0
-margin_bottom = 498.0
-
-[node name="Play Backward" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 503.0
-margin_right = 151.0
-margin_bottom = 517.0
-text = "Play Backward"
-
-[node name="play_backwards" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 500.0
-margin_right = 318.0
-margin_bottom = 520.0
-
-[node name="Empty10" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 500.0
-margin_right = 485.0
-margin_bottom = 520.0
-
-[node name="Play Farward" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 525.0
-margin_right = 151.0
-margin_bottom = 539.0
-text = "Play Farward"
-
-[node name="play_forward" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 522.0
-margin_right = 318.0
-margin_bottom = 542.0
-
-[node name="Empty11" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 522.0
-margin_right = 485.0
-margin_bottom = 542.0
-
-[node name="Next Frame" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 547.0
-margin_right = 151.0
-margin_bottom = 561.0
-text = "Next Frame"
-
-[node name="go_to_next_frame" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 544.0
-margin_right = 318.0
-margin_bottom = 564.0
-
-[node name="Empty12" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 544.0
-margin_right = 485.0
-margin_bottom = 564.0
-
-[node name="Last Frame" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 569.0
-margin_right = 151.0
-margin_bottom = 583.0
-text = "Jump to Last Frame"
-
-[node name="go_to_last_frame" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 566.0
-margin_right = 318.0
-margin_bottom = 586.0
-
-[node name="Empty13" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 566.0
-margin_right = 485.0
-margin_bottom = 586.0
-
-[node name="HSeparator7" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 588.0
-margin_right = 151.0
-margin_bottom = 592.0
-
-[node name="HSeparator8" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 588.0
-margin_right = 318.0
-margin_bottom = 592.0
-
-[node name="HSeparator9" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 588.0
-margin_right = 485.0
-margin_bottom = 592.0
-
-[node name="TitleMenu" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 594.0
-margin_right = 151.0
-margin_bottom = 608.0
-text = "Menu Shortcuts:"
-
-[node name="Empty14" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 594.0
-margin_right = 318.0
-margin_bottom = 608.0
-
-[node name="Empty15" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 594.0
-margin_right = 485.0
-margin_bottom = 608.0
-
-[node name="New File" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 613.0
-margin_right = 151.0
-margin_bottom = 627.0
-text = "New File"
-
-[node name="new_file" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 610.0
-margin_right = 318.0
-margin_bottom = 630.0
-
-[node name="Empty16" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 610.0
-margin_right = 485.0
-margin_bottom = 630.0
-
-[node name="Open File" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 635.0
-margin_right = 151.0
-margin_bottom = 649.0
-text = "Open File"
-
-[node name="open_file" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 632.0
-margin_right = 318.0
-margin_bottom = 652.0
-
-[node name="Empty17" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 632.0
-margin_right = 485.0
-margin_bottom = 652.0
-
-[node name="Save File" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 657.0
-margin_right = 151.0
-margin_bottom = 671.0
-text = "Save File"
-
-[node name="save_file" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 654.0
-margin_right = 318.0
-margin_bottom = 674.0
-
-[node name="Empty18" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 654.0
-margin_right = 485.0
-margin_bottom = 674.0
-
-[node name="Save File As" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 679.0
-margin_right = 151.0
-margin_bottom = 693.0
-text = "Save File As"
-
-[node name="save_file_as" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 676.0
-margin_right = 318.0
-margin_bottom = 696.0
-
-[node name="Empty19" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 676.0
-margin_right = 485.0
-margin_bottom = 696.0
-
-[node name="Export File" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 701.0
-margin_right = 151.0
-margin_bottom = 715.0
-text = "Export File"
-
-[node name="export_file" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 698.0
-margin_right = 318.0
-margin_bottom = 718.0
-
-[node name="Empty20" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 698.0
-margin_right = 485.0
-margin_bottom = 718.0
-
-[node name="Export File As" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 723.0
-margin_right = 151.0
-margin_bottom = 737.0
-text = "Export File As"
-
-[node name="export_file_as" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 720.0
-margin_right = 318.0
-margin_bottom = 740.0
-
-[node name="Empty21" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 720.0
-margin_right = 485.0
-margin_bottom = 740.0
-
-[node name="Quit" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 745.0
-margin_right = 151.0
-margin_bottom = 759.0
-text = "Quit"
-
-[node name="quit" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 742.0
-margin_right = 318.0
-margin_bottom = 762.0
-
-[node name="Empty22" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 742.0
-margin_right = 485.0
-margin_bottom = 762.0
-
-[node name="Undo" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 767.0
-margin_right = 151.0
-margin_bottom = 781.0
-text = "Undo"
-
-[node name="undo" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 764.0
-margin_right = 318.0
-margin_bottom = 784.0
-
-[node name="Empty23" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 764.0
-margin_right = 485.0
-margin_bottom = 784.0
-
-[node name="Redo" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 789.0
-margin_right = 151.0
-margin_bottom = 803.0
-text = "Redo"
-
-[node name="redo" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 786.0
-margin_right = 318.0
-margin_bottom = 806.0
-
-[node name="redo_secondary" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 786.0
-margin_right = 485.0
-margin_bottom = 806.0
-
-[node name="Copy" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 811.0
-margin_right = 151.0
-margin_bottom = 825.0
-text = "Copy"
-
-[node name="copy" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 808.0
-margin_right = 318.0
-margin_bottom = 828.0
-
-[node name="Empty24" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 808.0
-margin_right = 485.0
-margin_bottom = 828.0
-
-[node name="Cut" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 833.0
-margin_right = 151.0
-margin_bottom = 847.0
-text = "Cut"
-
-[node name="cut" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 830.0
-margin_right = 318.0
-margin_bottom = 850.0
-
-[node name="Empty25" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 830.0
-margin_right = 485.0
-margin_bottom = 850.0
-
-[node name="Paste" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 855.0
-margin_right = 151.0
-margin_bottom = 869.0
-text = "Paste"
-
-[node name="paste" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 852.0
-margin_right = 318.0
-margin_bottom = 872.0
-
-[node name="Empty26" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 852.0
-margin_right = 485.0
-margin_bottom = 872.0
-
-[node name="Delete" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 877.0
-margin_right = 151.0
-margin_bottom = 891.0
-text = "Delete"
-
-[node name="delete" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 874.0
-margin_right = 318.0
-margin_bottom = 894.0
-
-[node name="Empty27" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 874.0
-margin_right = 485.0
-margin_bottom = 894.0
-
-[node name="New Brush" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 899.0
-margin_right = 151.0
-margin_bottom = 913.0
-text = "New Brush"
-
-[node name="new_brush" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 896.0
-margin_right = 318.0
-margin_bottom = 916.0
-
-[node name="Empty28" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 896.0
-margin_right = 485.0
-margin_bottom = 916.0
-
-[node name="Select All" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 921.0
-margin_right = 151.0
-margin_bottom = 935.0
-text = "Select All"
-
-[node name="select_all" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 918.0
-margin_right = 318.0
-margin_bottom = 938.0
-
-[node name="Empty29" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 918.0
-margin_right = 485.0
-margin_bottom = 938.0
-
-[node name="Clear Selection" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 943.0
-margin_right = 151.0
-margin_bottom = 957.0
-text = "Clear Selection"
-
-[node name="clear_selection" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 940.0
-margin_right = 318.0
-margin_bottom = 960.0
-
-[node name="Empty30" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 940.0
-margin_right = 485.0
-margin_bottom = 960.0
-
-[node name="Invert Selection" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 965.0
-margin_right = 151.0
-margin_bottom = 979.0
-text = "Invert Selection"
-
-[node name="invert_selection" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 962.0
-margin_right = 318.0
-margin_bottom = 982.0
-
-[node name="Empty31" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 962.0
-margin_right = 485.0
-margin_bottom = 982.0
-
-[node name="Mirror View" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 987.0
-margin_right = 151.0
-margin_bottom = 1001.0
-text = "Mirror View"
-
-[node name="mirror_view" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 984.0
-margin_right = 318.0
-margin_bottom = 1004.0
-
-[node name="Empty32" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 984.0
-margin_right = 485.0
-margin_bottom = 1004.0
-
-[node name="Show Grid" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1009.0
-margin_right = 151.0
-margin_bottom = 1023.0
-text = "Show Grid"
-
-[node name="show_grid" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1006.0
-margin_right = 318.0
-margin_bottom = 1026.0
-
-[node name="Empty33" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1006.0
-margin_right = 485.0
-margin_bottom = 1026.0
-
-[node name="Show Pixel Grid" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1031.0
-margin_right = 151.0
-margin_bottom = 1045.0
-text = "Show Pixel Grid"
-
-[node name="show_pixel_grid" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1028.0
-margin_right = 318.0
-margin_bottom = 1048.0
-
-[node name="Empty34" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1028.0
-margin_right = 485.0
-margin_bottom = 1048.0
-
-[node name="Show Rulers" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1053.0
-margin_right = 151.0
-margin_bottom = 1067.0
-text = "Show Rulers"
-
-[node name="show_rulers" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1050.0
-margin_right = 318.0
-margin_bottom = 1070.0
-
-[node name="Empty35" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1050.0
-margin_right = 485.0
-margin_bottom = 1070.0
-
-[node name="Show Guides" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1075.0
-margin_right = 151.0
-margin_bottom = 1089.0
-text = "Show Guides"
-
-[node name="show_guides" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1072.0
-margin_right = 318.0
-margin_bottom = 1092.0
-
-[node name="Empty36" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1072.0
-margin_right = 485.0
-margin_bottom = 1092.0
-
-[node name="Edit Mode" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1097.0
-margin_right = 151.0
-margin_bottom = 1111.0
-text = "Edit Mode"
-
-[node name="edit_mode" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1094.0
-margin_right = 318.0
-margin_bottom = 1114.0
-
-[node name="Empty37" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1094.0
-margin_right = 485.0
-margin_bottom = 1114.0
-
-[node name="Zen Mode" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1119.0
-margin_right = 151.0
-margin_bottom = 1133.0
-text = "Zen Mode"
-
-[node name="zen_mode" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1116.0
-margin_right = 318.0
-margin_bottom = 1136.0
-
-[node name="Empty38" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1116.0
-margin_right = 485.0
-margin_bottom = 1136.0
-
-[node name="Fullscreen Mode" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1141.0
-margin_right = 151.0
-margin_bottom = 1155.0
-text = "Fullscreen Mode"
-
-[node name="toggle_fullscreen" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1138.0
-margin_right = 318.0
-margin_bottom = 1158.0
-
-[node name="Empty39" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1138.0
-margin_right = 485.0
-margin_bottom = 1158.0
-
-[node name="Online Docs" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 1163.0
-margin_right = 151.0
-margin_bottom = 1177.0
-text = "Online Documentation"
-
-[node name="open_docs" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 1160.0
-margin_right = 318.0
-margin_bottom = 1180.0
-
-[node name="Empty40" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 1160.0
-margin_right = 485.0
-margin_bottom = 1180.0
-
-[node name="HSeparator10" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 588.0
-margin_right = 151.0
-margin_bottom = 592.0
-
-[node name="HSeparator11" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 588.0
-margin_right = 318.0
-margin_bottom = 592.0
-
-[node name="HSeparator12" type="HSeparator" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 588.0
-margin_right = 485.0
-margin_bottom = 592.0
-
-[node name="TitleBehaviour" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 594.0
-margin_right = 151.0
-margin_bottom = 608.0
-text = "Special Behaviour:"
-
-[node name="Empty41" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 594.0
-margin_right = 318.0
-margin_bottom = 608.0
-
-[node name="Empty42" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 594.0
-margin_right = 485.0
-margin_bottom = 608.0
-
-[node name="Shift" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 613.0
-margin_right = 151.0
-margin_bottom = 627.0
-text = "Hold to create a 1:1 shape"
-
-[node name="shift" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 610.0
-margin_right = 318.0
-margin_bottom = 630.0
-
-[node name="Empty43" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 610.0
-margin_right = 485.0
-margin_bottom = 630.0
-
-[node name="Ctrl" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 613.0
-margin_right = 151.0
-margin_bottom = 627.0
-text = "Hold to center the shape on
-click origin / Snap selection to grid"
-
-[node name="ctrl" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 610.0
-margin_right = 318.0
-margin_bottom = 630.0
-
-[node name="Empty44" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 610.0
-margin_right = 485.0
-margin_bottom = 630.0
-
-[node name="Alt" type="Label" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_top = 613.0
-margin_right = 151.0
-margin_bottom = 627.0
-text = "Quick Color Picker /
-Hold to displace the shape's origin"
-
-[node name="alt" type="Button" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 156.0
-margin_top = 610.0
-margin_right = 318.0
-margin_bottom = 630.0
-
-[node name="Empty45" type="Control" parent="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/Shortcuts"]
-margin_left = 323.0
-margin_top = 610.0
-margin_right = 485.0
-margin_bottom = 630.0
+margin_right = 498.0
+margin_bottom = 56.0
[node name="Backup" type="VBoxContainer" parent="HSplitContainer/ScrollContainer/VBoxContainer"]
visible = false
@@ -2345,28 +1164,6 @@ color = Color( 0, 0, 0, 0 )
[node name="Popups" type="Node" parent="."]
-[node name="ShortcutSelector" type="ConfirmationDialog" parent="Popups"]
-margin_right = 250.0
-margin_bottom = 87.5
-rect_min_size = Vector2( 250, 87.5 )
-window_title = "Set the shortcut"
-dialog_text = "Press a key or a key combination to set the shortcut"
-dialog_hide_on_ok = false
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
-[node name="EnteredShortcut" type="Label" parent="Popups/ShortcutSelector"]
-margin_left = 8.0
-margin_top = 22.0
-margin_right = 341.0
-margin_bottom = 51.5
-align = 1
-valign = 1
-__meta__ = {
-"_edit_use_anchors_": false
-}
-
[node name="AddExtensionFileDialog" type="FileDialog" parent="Popups"]
margin_right = 429.0
margin_bottom = 356.0
@@ -2377,21 +1174,18 @@ mode = 1
access = 2
filters = PoolStringArray( "*.pck ; Godot Resource Pack File", "*.zip ;" )
show_hidden_files = true
-current_dir = ""
-current_path = ""
+current_dir = "/home/emmanouil/Documents/Orama/Pixelorama"
+current_path = "/home/emmanouil/Documents/Orama/Pixelorama/"
[connection signal="about_to_show" from="." to="." method="_on_PreferencesDialog_about_to_show"]
[connection signal="popup_hide" from="." to="." method="_on_PreferencesDialog_popup_hide"]
[connection signal="item_selected" from="HSplitContainer/List" to="." method="_on_List_item_selected"]
[connection signal="value_changed" from="HSplitContainer/ScrollContainer/VBoxContainer/Interface/ShrinkContainer/ShrinkHSlider" to="." method="_on_ShrinkHSlider_value_changed"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/Interface/ShrinkContainer/ShrinkApplyButton" to="." method="_on_ShrinkApplyButton_pressed"]
-[connection signal="item_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts/HBoxContainer/PresetOptionButton" to="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts" method="_on_PresetOptionButton_item_selected"]
[connection signal="item_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/InstalledExtensions" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_InstalledExtensions_item_selected"]
[connection signal="nothing_selected" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/InstalledExtensions" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_InstalledExtensions_nothing_selected"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/HBoxContainer/AddExtensionButton" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_AddExtensionButton_pressed"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/HBoxContainer/EnableButton" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_EnableButton_pressed"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/HBoxContainer/UninstallButton" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_UninstallButton_pressed"]
[connection signal="pressed" from="HSplitContainer/ScrollContainer/VBoxContainer/Extensions/HBoxContainer/OpenFolderButton" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_OpenFolderButton_pressed"]
-[connection signal="confirmed" from="Popups/ShortcutSelector" to="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts" method="_on_ShortcutSelector_confirmed"]
-[connection signal="popup_hide" from="Popups/ShortcutSelector" to="HSplitContainer/ScrollContainer/VBoxContainer/Shortcuts" method="_on_ShortcutSelector_popup_hide"]
[connection signal="files_selected" from="Popups/AddExtensionFileDialog" to="HSplitContainer/ScrollContainer/VBoxContainer/Extensions" method="_on_AddExtensionFileDialog_files_selected"]
diff --git a/src/Tools/Bucket.gd b/src/Tools/Bucket.gd
index f6a4d2859..c80af0a1c 100644
--- a/src/Tools/Bucket.gd
+++ b/src/Tools/Bucket.gd
@@ -22,13 +22,13 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
var options: OptionButton = $FillAreaOptions
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("change_tool_mode"):
_prev_mode = options.selected
- if event.is_action("ctrl"):
+ if event.is_action("change_tool_mode"):
options.selected = _prev_mode ^ 1
_fill_area = options.selected
$Similarity.visible = (_fill_area == 1)
- if event.is_action_released("ctrl"):
+ if event.is_action_released("change_tool_mode"):
options.selected = _prev_mode
_fill_area = options.selected
$Similarity.visible = (_fill_area == 1)
@@ -138,7 +138,7 @@ func update_pattern() -> void:
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_pick_color(position)
return
diff --git a/src/Tools/ColorPicker.gd b/src/Tools/ColorPicker.gd
index febd9202d..8aa08ec47 100644
--- a/src/Tools/ColorPicker.gd
+++ b/src/Tools/ColorPicker.gd
@@ -7,12 +7,12 @@ var _color_slot := 0
func _input(event: InputEvent) -> void:
var options: OptionButton = $ColorPicker/Options
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("change_tool_mode"):
_prev_mode = options.selected
- if event.is_action("ctrl"):
+ if event.is_action("change_tool_mode"):
options.selected = _prev_mode ^ 1
_color_slot = options.selected
- if event.is_action_released("ctrl"):
+ if event.is_action_released("change_tool_mode"):
options.selected = _prev_mode
_color_slot = options.selected
diff --git a/src/Tools/Draw.gd b/src/Tools/Draw.gd
index 817453c5d..86b2e1f20 100644
--- a/src/Tools/Draw.gd
+++ b/src/Tools/Draw.gd
@@ -523,7 +523,7 @@ func _line_angle_constraint(start: Vector2, end: Vector2) -> Dictionary:
var result := {}
var angle := rad2deg(end.angle_to_point(start))
var distance := start.distance_to(end)
- if Tools.control:
+ if Input.is_action_pressed("draw_snap_angle"):
if Tools.pixel_perfect:
angle = stepify(angle, 22.5)
if step_decimals(angle) != 0:
diff --git a/src/Tools/Eraser.gd b/src/Tools/Eraser.gd
index a0848c57e..b1f09afa7 100644
--- a/src/Tools/Eraser.gd
+++ b/src/Tools/Eraser.gd
@@ -36,7 +36,7 @@ func set_config(config: Dictionary) -> void:
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_picking_color = true
_pick_color(position)
return
@@ -50,7 +50,7 @@ func draw_start(position: Vector2) -> void:
prepare_undo("Draw")
_drawer.reset()
- _draw_line = Tools.shift
+ _draw_line = Input.is_action_pressed("draw_create_line")
if _draw_line:
_line_start = position
_line_end = position
@@ -65,7 +65,7 @@ func draw_start(position: Vector2) -> void:
func draw_move(position: Vector2) -> void:
.draw_move(position)
if _picking_color: # Still return even if we released Alt
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_pick_color(position)
return
diff --git a/src/Tools/LineTool.gd b/src/Tools/LineTool.gd
index b0fc38778..a1dae2968 100644
--- a/src/Tools/LineTool.gd
+++ b/src/Tools/LineTool.gd
@@ -61,15 +61,15 @@ func _get_shape_points_filled(_size: Vector2) -> PoolVector2Array:
func _input(event: InputEvent) -> void:
if _drawing:
- if event.is_action_pressed("alt"):
+ if event.is_action_pressed("shape_displace"):
_displace_origin = true
- elif event.is_action_released("alt"):
+ elif event.is_action_released("shape_displace"):
_displace_origin = false
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("shape_displace"):
_picking_color = true
_pick_color(position)
return
@@ -88,7 +88,7 @@ func draw_start(position: Vector2) -> void:
func draw_move(position: Vector2) -> void:
.draw_move(position)
if _picking_color: # Still return even if we released Alt
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("shape_displace"):
_pick_color(position)
return
@@ -97,8 +97,8 @@ func draw_move(position: Vector2) -> void:
_original_pos += position - _offset
var d = _line_angle_constraint(_original_pos, position)
_dest = d.position
- # Didn't use (Tools.control) to make more consistent with shape tools
- if Input.is_action_pressed("ctrl"):
+
+ if Input.is_action_pressed("shape_center"):
_start = _original_pos - (_dest - _original_pos)
else:
_start = _original_pos
@@ -202,7 +202,7 @@ func _line_angle_constraint(start: Vector2, end: Vector2) -> Dictionary:
var result := {}
var angle := rad2deg(end.angle_to_point(start))
var distance := start.distance_to(end)
- if Tools.shift:
+ if Input.is_action_pressed("shape_perfect"):
angle = stepify(angle, 22.5)
if step_decimals(angle) != 0:
var diff := end - start
diff --git a/src/Tools/Move.gd b/src/Tools/Move.gd
index 03221424f..3382f68c3 100644
--- a/src/Tools/Move.gd
+++ b/src/Tools/Move.gd
@@ -14,7 +14,7 @@ onready var selection_node: Node2D = Global.canvas.selection
func _input(event: InputEvent) -> void:
if _start_pos != Vector2.INF:
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("transform_snap_grid"):
_snap_to_grid = true
var grid_size := Vector2(Global.grid_width, Global.grid_height)
_offset = _offset.snapped(grid_size)
@@ -25,7 +25,7 @@ func _input(event: InputEvent) -> void:
selection_node.big_bounding_rectangle.position
- prev_pos
)
- elif event.is_action_released("ctrl"):
+ elif event.is_action_released("transform_snap_grid"):
_snap_to_grid = false
@@ -49,7 +49,7 @@ func draw_move(position: Vector2) -> void:
# while the content is being moved
if _content_transformation_check != selection_node.is_moving_content:
return
- if Tools.shift: # Snap to axis
+ if Input.is_action_pressed("transform_snap_axis"):
var angle := position.angle_to_point(_start_pos)
if abs(angle) <= PI / 4 or abs(angle) >= 3 * PI / 4:
position.y = _start_pos.y
@@ -74,7 +74,7 @@ func draw_end(position: Vector2) -> void:
_start_pos != Vector2.INF
and _content_transformation_check == selection_node.is_moving_content
):
- if Tools.shift: # Snap to axis
+ if Input.is_action_pressed("transform_snap_axis"):
var angle := position.angle_to_point(_start_pos)
if abs(angle) <= PI / 4 or abs(angle) >= 3 * PI / 4:
position.y = _start_pos.y
diff --git a/src/Tools/Pencil.gd b/src/Tools/Pencil.gd
index 7a1189152..b8e80c474 100644
--- a/src/Tools/Pencil.gd
+++ b/src/Tools/Pencil.gd
@@ -40,12 +40,12 @@ func _on_FillInside_toggled(button_pressed):
func _input(event: InputEvent) -> void:
var overwrite_button: CheckBox = $Overwrite
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("change_tool_mode"):
_prev_mode = overwrite_button.pressed
- if event.is_action("ctrl"):
+ if event.is_action("change_tool_mode"):
overwrite_button.pressed = !_prev_mode
_overwrite = overwrite_button.pressed
- if event.is_action_released("ctrl"):
+ if event.is_action_released("change_tool_mode"):
overwrite_button.pressed = _prev_mode
_overwrite = overwrite_button.pressed
@@ -71,7 +71,7 @@ func update_config() -> void:
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_picking_color = true
_pick_color(position)
return
@@ -90,7 +90,7 @@ func draw_start(position: Vector2) -> void:
prepare_undo("Draw")
_drawer.reset()
- _draw_line = Tools.shift
+ _draw_line = Input.is_action_pressed("draw_create_line")
if _draw_line:
_line_start = position
_line_end = position
@@ -107,7 +107,7 @@ func draw_start(position: Vector2) -> void:
func draw_move(position: Vector2) -> void:
.draw_move(position)
if _picking_color: # Still return even if we released Alt
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_pick_color(position)
return
diff --git a/src/Tools/SelectionTools/EllipseSelect.gd b/src/Tools/SelectionTools/EllipseSelect.gd
index bf83724d0..d4bab8da2 100644
--- a/src/Tools/SelectionTools/EllipseSelect.gd
+++ b/src/Tools/SelectionTools/EllipseSelect.gd
@@ -10,17 +10,17 @@ var _displace_origin = false # Mouse Click + Alt
func _input(event: InputEvent) -> void:
._input(event)
if !_move and !_rect.has_no_area():
- if event.is_action_pressed("shift"):
+ if event.is_action_pressed("shape_perfect"):
_square = true
- elif event.is_action_released("shift"):
+ elif event.is_action_released("shape_perfect"):
_square = false
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("shape_center"):
_expand_from_center = true
- elif event.is_action_released("ctrl"):
+ elif event.is_action_released("shape_center"):
_expand_from_center = false
- if event.is_action_pressed("alt"):
+ if event.is_action_pressed("shape_displace"):
_displace_origin = true
- elif event.is_action_released("alt"):
+ elif event.is_action_released("shape_displace"):
_displace_origin = false
diff --git a/src/Tools/SelectionTools/PolygonSelect.gd b/src/Tools/SelectionTools/PolygonSelect.gd
index 2172a397f..9a81c8de9 100644
--- a/src/Tools/SelectionTools/PolygonSelect.gd
+++ b/src/Tools/SelectionTools/PolygonSelect.gd
@@ -17,8 +17,8 @@ func _input(event: InputEvent) -> void:
append_gap(_draw_points[-1], _draw_points[0], _draw_points)
_ready_to_apply = true
apply_selection(Vector2.ZERO) # Argument doesn't matter
- elif event is InputEventKey:
- if event.is_action_pressed("escape") and _ongoing_selection:
+ else:
+ if event.is_action_pressed("cancel") and _ongoing_selection:
_ongoing_selection = false
_draw_points.clear()
_ready_to_apply = false
diff --git a/src/Tools/SelectionTools/RectSelect.gd b/src/Tools/SelectionTools/RectSelect.gd
index 2d21f9b66..a89f8cbc5 100644
--- a/src/Tools/SelectionTools/RectSelect.gd
+++ b/src/Tools/SelectionTools/RectSelect.gd
@@ -10,17 +10,17 @@ var _displace_origin = false # Mouse Click + Alt
func _input(event: InputEvent) -> void:
._input(event)
if !_move and !_rect.has_no_area():
- if event.is_action_pressed("shift"):
+ if event.is_action_pressed("shape_perfect"):
_square = true
- elif event.is_action_released("shift"):
+ elif event.is_action_released("shape_perfect"):
_square = false
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("shape_center"):
_expand_from_center = true
- elif event.is_action_released("ctrl"):
+ elif event.is_action_released("shape_center"):
_expand_from_center = false
- if event.is_action_pressed("alt"):
+ if event.is_action_pressed("shape_displace"):
_displace_origin = true
- elif event.is_action_released("alt"):
+ elif event.is_action_released("shape_displace"):
_displace_origin = false
diff --git a/src/Tools/SelectionTools/SelectionTool.gd b/src/Tools/SelectionTools/SelectionTool.gd
index 2a2437b2f..afda880d5 100644
--- a/src/Tools/SelectionTools/SelectionTool.gd
+++ b/src/Tools/SelectionTools/SelectionTool.gd
@@ -33,7 +33,7 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
if _move:
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("transform_snap_grid"):
_snap_to_grid = true
var grid_size := Vector2(Global.grid_width, Global.grid_height)
_offset = _offset.snapped(grid_size)
@@ -43,7 +43,7 @@ func _input(event: InputEvent) -> void:
selection_node.big_bounding_rectangle.position
- prev_pos
)
- elif event.is_action_released("ctrl"):
+ elif event.is_action_released("transform_snap_grid"):
_snap_to_grid = false
@@ -66,9 +66,9 @@ func draw_start(position: Vector2) -> void:
return
var project: Project = Global.current_project
undo_data = selection_node.get_undo_data(false)
- _intersect = Tools.shift && Tools.control
- _add = Tools.shift && !_intersect
- _subtract = Tools.control && !_intersect
+ _intersect = Input.is_action_pressed("selection_intersect", true)
+ _add = Input.is_action_pressed("selection_add", true)
+ _subtract = Input.is_action_pressed("selection_subtract", true)
_start_pos = position
_offset = position
@@ -78,59 +78,57 @@ func draw_start(position: Vector2) -> void:
offsetted_pos.x -= selection_position.x
if selection_position.y < 0:
offsetted_pos.y -= selection_position.y
+
+ var quick_copy: bool = Input.is_action_pressed("transform_copy_selection_content", true)
if (
offsetted_pos.x >= 0
and offsetted_pos.y >= 0
and project.selection_bitmap.get_bit(offsetted_pos)
- and (!Tools.control or Tools.alt)
- and !Tools.shift
+ and (!_add and !_subtract and !_intersect or quick_copy)
and !_ongoing_selection
):
if !Global.current_project.layers[Global.current_project.current_layer].can_layer_get_drawn():
return
# Move current selection
_move = true
- if Tools.alt: # Move selection without content
- if Tools.control:
- # Move the selection without cutting it from the original position
- # (makes a quick copy of it)
- _move_content = true
- if selection_node.is_moving_content:
- for image in _get_selected_draw_images():
- image.blit_rect_mask(
- selection_node.preview_image,
- selection_node.preview_image,
- Rect2(Vector2.ZERO, project.selection_bitmap.get_size()),
- selection_node.big_bounding_rectangle.position
- )
+ if quick_copy: # Move selection without cutting it from the original position (quick copy)
+ _move_content = true
+ if selection_node.is_moving_content:
+ for image in _get_selected_draw_images():
+ image.blit_rect_mask(
+ selection_node.preview_image,
+ selection_node.preview_image,
+ Rect2(Vector2.ZERO, project.selection_bitmap.get_size()),
+ selection_node.big_bounding_rectangle.position
+ )
- var selected_bitmap_copy = project.selection_bitmap.duplicate()
- project.move_bitmap_values(selected_bitmap_copy)
-
- project.selection_bitmap = selected_bitmap_copy
- selection_node.commit_undo("Move Selection", selection_node.undo_data)
- selection_node.undo_data = selection_node.get_undo_data(true)
- else:
- selection_node.transform_content_start()
- selection_node.clear_in_selected_cels = false
- for image in _get_selected_draw_images():
- image.blit_rect_mask(
- selection_node.preview_image,
- selection_node.preview_image,
- Rect2(Vector2.ZERO, project.selection_bitmap.get_size()),
- selection_node.big_bounding_rectangle.position
- )
- Global.canvas.update_selected_cels_textures()
+ var selected_bitmap_copy = project.selection_bitmap.duplicate()
+ project.move_bitmap_values(selected_bitmap_copy)
+ project.selection_bitmap = selected_bitmap_copy
+ selection_node.commit_undo("Move Selection", selection_node.undo_data)
+ selection_node.undo_data = selection_node.get_undo_data(true)
else:
- selection_node.transform_content_confirm()
- _move_content = false
- selection_node.move_borders_start()
- else:
+ selection_node.transform_content_start()
+ selection_node.clear_in_selected_cels = false
+ for image in _get_selected_draw_images():
+ image.blit_rect_mask(
+ selection_node.preview_image,
+ selection_node.preview_image,
+ Rect2(Vector2.ZERO, project.selection_bitmap.get_size()),
+ selection_node.big_bounding_rectangle.position
+ )
+ Global.canvas.update_selected_cels_textures()
+
+ elif Input.is_action_pressed("transform_move_selection_only", true): # Doesn't move content
+ selection_node.transform_content_confirm()
+ _move_content = false
+ selection_node.move_borders_start()
+ else: # Move selection and content normally
_move_content = true
selection_node.transform_content_start()
- else:
+ else: # No moving
selection_node.transform_content_confirm()
_content_transformation_check = selection_node.is_moving_content
@@ -145,7 +143,7 @@ func draw_move(position: Vector2) -> void:
if _content_transformation_check != selection_node.is_moving_content:
return
if _move:
- if Tools.shift: # Snap to axis
+ if Input.is_action_pressed("transform_snap_axis"): # Snap to axis
var angle := position.angle_to_point(_start_pos)
if abs(angle) <= PI / 4 or abs(angle) >= 3 * PI / 4:
position.y = _start_pos.y
diff --git a/src/Tools/Shading.gd b/src/Tools/Shading.gd
index e6d46b96f..883d5be87 100644
--- a/src/Tools/Shading.gd
+++ b/src/Tools/Shading.gd
@@ -111,13 +111,13 @@ func _init() -> void:
func _input(event: InputEvent) -> void:
var options: OptionButton = $LightenDarken
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("change_tool_mode"):
_prev_mode = options.selected
- if event.is_action("ctrl"):
+ if event.is_action("change_tool_mode"):
options.selected = _prev_mode ^ 1
_mode = options.selected
_drawer.color_op.lighten_or_darken = _mode
- if event.is_action_released("ctrl"):
+ if event.is_action_released("change_tool_mode"):
options.selected = _prev_mode
_mode = options.selected
_drawer.color_op.lighten_or_darken = _mode
@@ -211,7 +211,7 @@ func update_strength() -> void:
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_picking_color = true
_pick_color(position)
return
@@ -225,7 +225,7 @@ func draw_start(position: Vector2) -> void:
prepare_undo("Draw")
_drawer.reset()
- _draw_line = Tools.shift
+ _draw_line = Input.is_action_pressed("draw_create_line")
if _draw_line:
_line_start = position
_line_end = position
@@ -240,7 +240,7 @@ func draw_start(position: Vector2) -> void:
func draw_move(position: Vector2) -> void:
.draw_move(position)
if _picking_color: # Still return even if we released Alt
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("draw_color_picker"):
_pick_color(position)
return
diff --git a/src/Tools/ShapeDrawer.gd b/src/Tools/ShapeDrawer.gd
index ded574b20..7c7a327b2 100644
--- a/src/Tools/ShapeDrawer.gd
+++ b/src/Tools/ShapeDrawer.gd
@@ -76,15 +76,15 @@ func _get_shape_points_filled(_size: Vector2) -> PoolVector2Array:
func _input(event: InputEvent) -> void:
if _drawing:
- if event.is_action_pressed("alt"):
+ if event.is_action_pressed("shape_displace"):
_displace_origin = true
- elif event.is_action_released("alt"):
+ elif event.is_action_released("shape_displace"):
_displace_origin = false
func draw_start(position: Vector2) -> void:
.draw_start(position)
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("shape_displace"):
_picking_color = true
_pick_color(position)
return
@@ -102,7 +102,7 @@ func draw_start(position: Vector2) -> void:
func draw_move(position: Vector2) -> void:
.draw_move(position)
if _picking_color: # Still return even if we released Alt
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("shape_displace"):
_pick_color(position)
return
@@ -165,15 +165,13 @@ func _draw_shape(origin: Vector2, dest: Vector2) -> void:
# Given an origin point and destination point, returns a rect representing
# where the shape will be drawn and what is its size
func _get_result_rect(origin: Vector2, dest: Vector2) -> Rect2:
- # WARNING: Don't replace Input.is_action_pressed for Tools.control,
- # it makes the preview jittery on Windows
var rect := Rect2(Vector2.ZERO, Vector2.ZERO)
# Center the rect on the mouse
- if Input.is_action_pressed("ctrl"):
+ if Input.is_action_pressed("shape_center"):
var new_size := (dest - origin).floor()
# Make rect 1:1 while centering it on the mouse
- if Input.is_action_pressed("shift"):
+ if Input.is_action_pressed("shape_perfect"):
var square_size := max(abs(new_size.x), abs(new_size.y))
new_size = Vector2(square_size, square_size)
@@ -181,7 +179,7 @@ func _get_result_rect(origin: Vector2, dest: Vector2) -> Rect2:
dest = origin + 2 * new_size
# Make rect 1:1 while not trying to center it
- if Input.is_action_pressed("shift"):
+ if Input.is_action_pressed("shape_perfect"):
var square_size := min(abs(origin.x - dest.x), abs(origin.y - dest.y))
rect.position.x = origin.x if origin.x < dest.x else origin.x - square_size
rect.position.y = origin.y if origin.y < dest.y else origin.y - square_size
diff --git a/src/Tools/Zoom.gd b/src/Tools/Zoom.gd
index bc14baa8c..e1db1cfed 100644
--- a/src/Tools/Zoom.gd
+++ b/src/Tools/Zoom.gd
@@ -9,12 +9,12 @@ func _input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
_relative = event.relative
- if event.is_action_pressed("ctrl"):
+ if event.is_action_pressed("change_tool_mode"):
_prev_mode = $ModeOptions.selected
- if event.is_action("ctrl"):
+ if event.is_action("change_tool_mode"):
$ModeOptions.selected = _prev_mode ^ 1
_zoom_mode = $ModeOptions.selected
- if event.is_action_released("ctrl"):
+ if event.is_action_released("change_tool_mode"):
$ModeOptions.selected = _prev_mode
_zoom_mode = $ModeOptions.selected
diff --git a/src/UI/Canvas/CameraMovement.gd b/src/UI/Canvas/CameraMovement.gd
index 70f31bfb1..39fd6c7c5 100644
--- a/src/UI/Canvas/CameraMovement.gd
+++ b/src/UI/Canvas/CameraMovement.gd
@@ -1,22 +1,10 @@
extends Camera2D
enum Cameras { MAIN, SECOND, SMALL }
-enum Direction { UP, DOWN, LEFT, RIGHT }
-const LOW_SPEED_MOVE_RATE := 150.0
-const MEDIUM_SPEED_MOVE_RATE := 750.0
-const HIGH_SPEED_MOVE_RATE := 3750.0
-const KEY_MOVE_ACTION_NAMES := ["ui_up", "ui_down", "ui_left", "ui_right"]
-# Holds sign multipliers for the given directions nyaa
-# (per the indices defined by Direction)
-# UP, DOWN, LEFT, RIGHT in that order
-const DIRECTIONAL_SIGN_MULTIPLIERS := [
- Vector2(0.0, -1.0), Vector2(0.0, 1.0), Vector2(-1.0, 0.0), Vector2(1.0, 0.0)
-]
+const KEY_MOVE_ACTION_NAMES := ["camera_left", "camera_right", "camera_up", "camera_down"]
+const CAMERA_SPEED_RATE := 15.0
-# Indices are as in the Direction enum
-# This is the total time the key for that direction has been pressed.
-var key_move_press_time := [0.0, 0.0, 0.0, 0.0]
var tween: Tween
var zoom_min := Vector2(0.005, 0.005)
var zoom_max := Vector2.ONE
@@ -106,90 +94,6 @@ func update_transparent_checker_offset() -> void:
transparent_checker.update_offset(o, s)
-# Get the speed multiplier for when you've pressed
-# a movement key for the given amount of time
-func _dir_move_zoom_multiplier(press_time: float) -> float:
- if press_time < 0:
- return 0.0
- if Input.is_key_pressed(KEY_SHIFT) and Input.is_key_pressed(KEY_CONTROL):
- return HIGH_SPEED_MOVE_RATE
- elif Input.is_key_pressed(KEY_SHIFT):
- return MEDIUM_SPEED_MOVE_RATE
- elif !Input.is_key_pressed(KEY_CONTROL):
- # control + right/left is used to move frames so
- # we do this check to ensure that there is no conflict
- return LOW_SPEED_MOVE_RATE
- else:
- return 0.0
-
-
-func _reset_dir_move_time(direction) -> void:
- key_move_press_time[direction] = 0.0
-
-
-# Check if an event is a ui_up/down/left/right event-press :)
-func _is_action_direction_pressed(event: InputEvent, allow_echo: bool = true) -> bool:
- for slot in Tools._slots.values():
- if slot.tool_node is SelectionTool:
- return false
- for action in KEY_MOVE_ACTION_NAMES:
- if event.is_action_pressed(action, allow_echo):
- return true
- return false
-
-
-# Check if an event is a ui_up/down/left/right event release nya
-func _is_action_direction_released(event: InputEvent) -> bool:
- for slot in Tools._slots.values():
- if slot.tool_node is SelectionTool:
- return false
- for action in KEY_MOVE_ACTION_NAMES:
- if event.is_action_released(action):
- return true
- return false
-
-
-# get the Direction associated with the event.
-# if not a direction event return null
-func _get_action_direction(event: InputEvent): # -> Optional[Direction]
- if event.is_action("ui_up"):
- return Direction.UP
- elif event.is_action("ui_down"):
- return Direction.DOWN
- elif event.is_action("ui_left"):
- return Direction.LEFT
- elif event.is_action("ui_right"):
- return Direction.RIGHT
- return null
-
-
-# Process an action event for a pressed direction
-# action
-func _process_direction_action_pressed(event: InputEvent) -> void:
- var dir = _get_action_direction(event)
- if dir == null:
- return
- var increment := get_process_delta_time()
- # Count the total time we've been doing this ^.^
- key_move_press_time[dir] += increment
- var this_direction_press_time: float = key_move_press_time[dir]
- var move_speed := _dir_move_zoom_multiplier(this_direction_press_time)
- offset = (
- offset
- + move_speed * increment * DIRECTIONAL_SIGN_MULTIPLIERS[dir].rotated(rotation) * zoom
- )
- _update_rulers()
- update_transparent_checker_offset()
-
-
-# Process an action for a release direction action
-func _process_direction_action_released(event: InputEvent) -> void:
- var dir = _get_action_direction(event)
- if dir == null:
- return
- _reset_dir_move_time(dir)
-
-
func _input(event: InputEvent) -> void:
if !Global.can_draw:
drag = false
@@ -200,14 +104,26 @@ func _input(event: InputEvent) -> void:
drag = false
return
- if event.is_action_pressed("middle_mouse") || event.is_action_pressed("space"):
+ var get_velocity := false
+ for action in KEY_MOVE_ACTION_NAMES:
+ if event.is_action(action):
+ get_velocity = true
+
+ if get_velocity:
+ var velocity := Input.get_vector("camera_left", "camera_right", "camera_up", "camera_down")
+ if velocity != Vector2.ZERO and !_has_selection_tool():
+ offset += velocity.rotated(rotation) * zoom * CAMERA_SPEED_RATE
+ _update_rulers()
+
+ if event.is_action_pressed("pan"):
drag = true
- elif event.is_action_released("middle_mouse") || event.is_action_released("space"):
+ elif event.is_action_released("pan"):
drag = false
elif event.is_action_pressed("zoom_in"): # Wheel Up Event
zoom_camera(-1)
elif event.is_action_pressed("zoom_out"): # Wheel Down Event
zoom_camera(1)
+
elif event is InputEventMagnifyGesture: # Zoom Gesture on a Laptop touchpad
if event.factor < 1:
zoom_camera(1)
@@ -220,15 +136,17 @@ func _input(event: InputEvent) -> void:
offset = offset - event.relative.rotated(rotation) * zoom
update_transparent_checker_offset()
_update_rulers()
- elif event is InputEventKey:
- if _is_action_direction_pressed(event):
- _process_direction_action_pressed(event)
- elif _is_action_direction_released(event):
- _process_direction_action_released(event)
save_values_to_project()
+func _has_selection_tool() -> bool:
+ for slot in Tools._slots.values():
+ if slot.tool_node is SelectionTool:
+ return true
+ return false
+
+
# Rotate Camera
func _rotate_camera_around_point(degrees: float, point: Vector2) -> void:
offset = (offset - point).rotated(deg2rad(degrees)) + point
diff --git a/src/UI/Canvas/Previews.gd b/src/UI/Canvas/Previews.gd
index b9db7c26c..e2fbcc8d6 100644
--- a/src/UI/Canvas/Previews.gd
+++ b/src/UI/Canvas/Previews.gd
@@ -3,12 +3,7 @@ extends Node2D
func _input(event: InputEvent) -> void:
if Global.has_focus:
- if (
- event is InputEventMouse
- or event.is_action("shift")
- or event.is_action("ctrl")
- or event.is_action("alt")
- ):
+ if event is InputEventMouse or event is InputEventKey:
update()
diff --git a/src/UI/Canvas/Selection.gd b/src/UI/Canvas/Selection.gd
index 9788bd726..5d35758c5 100644
--- a/src/UI/Canvas/Selection.gd
+++ b/src/UI/Canvas/Selection.gd
@@ -81,13 +81,13 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
- if event is InputEventKey:
- if is_moving_content:
- if Input.is_action_just_pressed("enter"):
- transform_content_confirm()
- elif Input.is_action_just_pressed("escape"):
- transform_content_cancel()
+ if is_moving_content:
+ if Input.is_action_just_pressed("confirm"):
+ transform_content_confirm()
+ elif Input.is_action_just_pressed("cancel"):
+ transform_content_cancel()
+ if event is InputEventKey:
_move_with_arrow_keys(event)
elif event is InputEventMouse:
@@ -116,10 +116,10 @@ func _input(event: InputEvent) -> void:
Global.has_focus = false
mouse_pos_on_gizmo_drag = Global.canvas.current_pixel
dragged_gizmo = gizmo
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("transform_move_selection_only"):
transform_content_confirm()
if !is_moving_content:
- if Input.is_action_pressed("alt"):
+ if Input.is_action_pressed("transform_move_selection_only"):
undo_data = get_undo_data(false)
temp_rect = big_bounding_rectangle
temp_bitmap = Global.current_project.selection_bitmap
@@ -301,7 +301,7 @@ func update_on_zoom(zoom: float) -> void:
func _gizmo_resize() -> void:
var dir := dragged_gizmo.direction
- if Input.is_action_pressed("ctrl"):
+ if Input.is_action_pressed("shape_center"):
# Code inspired from https://github.com/GDQuest/godot-open-rpg
if dir.x != 0 and dir.y != 0: # Border gizmos
temp_rect.size = ((Global.canvas.current_pixel - temp_rect_pivot) * 2.0 * dir)
@@ -314,7 +314,7 @@ func _gizmo_resize() -> void:
else:
_resize_rect(Global.canvas.current_pixel, dir)
- if Input.is_action_pressed("shift"): # Maintain aspect ratio
+ if Input.is_action_pressed("shape_perfect"): # Maintain aspect ratio
var end_y = temp_rect.end.y
if dir == Vector2(1, -1) or dir.x == 0: # Top right corner, center top and center bottom
var size := temp_rect.size.y
diff --git a/src/UI/ColorPickers.tscn b/src/UI/ColorPickers.tscn
index 6762b96ca..18d363479 100644
--- a/src/UI/ColorPickers.tscn
+++ b/src/UI/ColorPickers.tscn
@@ -1,10 +1,14 @@
-[gd_scene load_steps=5 format=2]
+[gd_scene load_steps=6 format=2]
[ext_resource path="res://assets/graphics/misc/color_defaults.png" type="Texture" id=1]
[ext_resource path="res://assets/graphics/misc/color_switch.png" type="Texture" id=2]
[ext_resource path="res://src/UI/ColorPickers.gd" type="Script" id=3]
+[sub_resource type="InputEventAction" id=20]
+action = "switch_colors"
+
[sub_resource type="ShortCut" id=19]
+shortcut = SubResource( 20 )
[node name="ColorPickers" type="PanelContainer"]
margin_left = 958.0
diff --git a/src/UI/Dialogs/AboutDialog.tscn b/src/UI/Dialogs/AboutDialog.tscn
index f0b7846c0..f7e021acd 100644
--- a/src/UI/Dialogs/AboutDialog.tscn
+++ b/src/UI/Dialogs/AboutDialog.tscn
@@ -34,7 +34,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", "Portions of this software are copyright © 2021 The FreeType Project (www.freetype.org). All rights reserved.", "MIT License
-Copyright (c) 2020 Igor Santarek
+Copyright (c) 2022 Orama Interactive
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -161,7 +161,15 @@ express Statement of Purpose.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
-" ]
+", "MIT License
+
+Copyright (c) 2020 Igor Santarek
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ]
[node name="AboutUI" type="VBoxContainer" parent="."]
anchor_right = 1.0
@@ -376,13 +384,14 @@ mouse_default_cursor_shape = 2
group = SubResource( 1 )
text = "FreeType"
-[node name="godot-gdgifexporterLicense" type="CheckBox" parent="AboutUI/Credits/Licenses/LicenseButtonsContainer"]
+[node name="keychainLicense" type="CheckBox" parent="AboutUI/Credits/Licenses/LicenseButtonsContainer"]
+margin_left = 157.0
margin_top = 28.0
-margin_right = 153.0
+margin_right = 348.0
margin_bottom = 52.0
mouse_default_cursor_shape = 2
group = SubResource( 1 )
-text = "godot-gdgifexporter"
+text = "Keychain"
[node name="godot-dockable-containerLicense" type="CheckBox" parent="AboutUI/Credits/Licenses/LicenseButtonsContainer"]
margin_left = 157.0
@@ -393,6 +402,14 @@ mouse_default_cursor_shape = 2
group = SubResource( 1 )
text = "godot-dockable-container"
+[node name="godot-gdgifexporterLicense" type="CheckBox" parent="AboutUI/Credits/Licenses/LicenseButtonsContainer"]
+margin_top = 28.0
+margin_right = 153.0
+margin_bottom = 52.0
+mouse_default_cursor_shape = 2
+group = SubResource( 1 )
+text = "godot-gdgifexporter"
+
[node name="HSeparator2" type="HSeparator" parent="AboutUI"]
margin_top = 349.0
margin_right = 576.0
diff --git a/src/UI/Timeline/AnimationTimeline.tscn b/src/UI/Timeline/AnimationTimeline.tscn
index 64f456e29..c3cf08e3b 100644
--- a/src/UI/Timeline/AnimationTimeline.tscn
+++ b/src/UI/Timeline/AnimationTimeline.tscn
@@ -36,49 +36,41 @@ corner_radius_bottom_right = 5
corner_radius_bottom_left = 5
expand_margin_bottom = 32.0
-[sub_resource type="InputEventKey" id=3]
-control = true
-command = true
-scancode = 16777229
+[sub_resource type="InputEventAction" id=21]
+action = "go_to_first_frame"
[sub_resource type="ShortCut" id=4]
-shortcut = SubResource( 3 )
+shortcut = SubResource( 21 )
-[sub_resource type="InputEventKey" id=5]
-control = true
-command = true
-scancode = 16777231
+[sub_resource type="InputEventAction" id=23]
+action = "go_to_previous_frame"
[sub_resource type="ShortCut" id=6]
-shortcut = SubResource( 5 )
+shortcut = SubResource( 23 )
-[sub_resource type="InputEventKey" id=7]
-scancode = 16777247
+[sub_resource type="InputEventAction" id=24]
+action = "play_backwards"
[sub_resource type="ShortCut" id=8]
-shortcut = SubResource( 7 )
+shortcut = SubResource( 24 )
-[sub_resource type="InputEventKey" id=9]
-scancode = 16777248
+[sub_resource type="InputEventAction" id=25]
+action = "play_forward"
[sub_resource type="ShortCut" id=10]
-shortcut = SubResource( 9 )
+shortcut = SubResource( 25 )
-[sub_resource type="InputEventKey" id=11]
-control = true
-command = true
-scancode = 16777233
+[sub_resource type="InputEventAction" id=26]
+action = "go_to_next_frame"
[sub_resource type="ShortCut" id=12]
-shortcut = SubResource( 11 )
+shortcut = SubResource( 26 )
-[sub_resource type="InputEventKey" id=13]
-control = true
-command = true
-scancode = 16777230
+[sub_resource type="InputEventAction" id=22]
+action = "go_to_last_frame"
[sub_resource type="ShortCut" id=14]
-shortcut = SubResource( 13 )
+shortcut = SubResource( 22 )
[sub_resource type="StyleBoxEmpty" id=15]
diff --git a/src/UI/ToolButtons.gd b/src/UI/ToolButtons.gd
index 38180fb6e..a6ab2396e 100644
--- a/src/UI/ToolButtons.gd
+++ b/src/UI/ToolButtons.gd
@@ -9,7 +9,7 @@ func _ready() -> void:
func _input(event: InputEvent) -> void:
if not Global.has_focus or not event is InputEventKey:
return
- for action in ["undo", "redo", "redo_secondary"]:
+ for action in ["undo", "redo"]:
if event.is_action_pressed(action):
return
diff --git a/src/UI/TopMenuContainer.gd b/src/UI/TopMenuContainer.gd
index 3214c214d..186d80c4c 100644
--- a/src/UI/TopMenuContainer.gd
+++ b/src/UI/TopMenuContainer.gd
@@ -1,42 +1,5 @@
extends Panel
-enum FileMenuId { NEW, OPEN, OPEN_LAST_PROJECT, SAVE, SAVE_AS, EXPORT, EXPORT_AS, QUIT }
-enum EditMenuId { UNDO, REDO, COPY, CUT, PASTE, DELETE, NEW_BRUSH, PREFERENCES }
-enum ViewMenuId {
- TILE_MODE,
- GREYSCALE_VIEW,
- MIRROR_VIEW,
- SHOW_GRID,
- SHOW_PIXEL_GRID,
- SHOW_RULERS,
- SHOW_GUIDES,
-}
-enum WindowMenuId { WINDOW_OPACITY, PANELS, LAYOUTS, ZEN_MODE, FULLSCREEN_MODE }
-enum ImageMenuId {
- SCALE_IMAGE,
- CENTRALIZE_IMAGE,
- CROP_IMAGE,
- RESIZE_CANVAS,
- FLIP,
- ROTATE,
- INVERT_COLORS,
- DESATURATION,
- OUTLINE,
- DROP_SHADOW,
- HSV,
- GRADIENT,
- SHADER
-}
-enum SelectMenuId { SELECT_ALL, CLEAR_SELECTION, INVERT }
-enum HelpMenuId {
- VIEW_SPLASH_SCREEN,
- ONLINE_DOCS,
- ISSUE_TRACKER,
- OPEN_LOGS_FOLDER,
- CHANGELOG,
- ABOUT_PIXELORAMA
-}
-
var file_menu: PopupMenu
var view_menu: PopupMenu
var window_menu: PopupMenu
@@ -81,32 +44,32 @@ func _ready() -> void:
func _setup_file_menu() -> void:
- var file_menu_items := { # order as in FileMenuId enum
- "New...": InputMap.get_action_list("new_file")[0].get_scancode_with_modifiers(),
- "Open...": InputMap.get_action_list("open_file")[0].get_scancode_with_modifiers(),
- "Open last project...": 0,
- "Recent projects": 0,
- "Save...": InputMap.get_action_list("save_file")[0].get_scancode_with_modifiers(),
- "Save as...": InputMap.get_action_list("save_file_as")[0].get_scancode_with_modifiers(),
- "Export...": InputMap.get_action_list("export_file")[0].get_scancode_with_modifiers(),
- "Export as...": InputMap.get_action_list("export_file_as")[0].get_scancode_with_modifiers(),
- "Quit": InputMap.get_action_list("quit")[0].get_scancode_with_modifiers(),
- }
+ # Order as in FileMenu enum
+ var file_menu_items := [
+ "New...",
+ "Open...",
+ "Open last project...",
+ "Recent projects",
+ "Save...",
+ "Save as...",
+ "Export...",
+ "Export as...",
+ "Quit",
+ ]
file_menu = file_menu_button.get_popup()
var i := 0
-
- for item in file_menu_items.keys():
+ for item in file_menu_items:
if item == "Recent projects":
_setup_recent_projects_submenu(item)
else:
- file_menu.add_item(item, i, file_menu_items[item])
- i += 1
+ file_menu.add_item(item, i)
+ i += 1
file_menu.connect("id_pressed", self, "file_menu_id_pressed")
if OS.get_name() == "HTML5":
- file_menu.set_item_disabled(FileMenuId.OPEN_LAST_PROJECT, true)
- file_menu.set_item_disabled(FileMenuId.SAVE, true)
+ file_menu.set_item_disabled(Global.FileMenu.OPEN_LAST_PROJECT, true)
+ file_menu.set_item_disabled(Global.FileMenu.SAVE, true)
func _setup_recent_projects_submenu(item: String) -> void:
@@ -124,21 +87,14 @@ func update_recent_projects_submenu() -> void:
func _setup_edit_menu() -> void:
- var edit_menu_items := { # order as in EditMenuId enum
- "Undo": InputMap.get_action_list("undo")[0].get_scancode_with_modifiers(),
- "Redo": InputMap.get_action_list("redo")[0].get_scancode_with_modifiers(),
- "Copy": InputMap.get_action_list("copy")[0].get_scancode_with_modifiers(),
- "Cut": InputMap.get_action_list("cut")[0].get_scancode_with_modifiers(),
- "Paste": InputMap.get_action_list("paste")[0].get_scancode_with_modifiers(),
- "Delete": InputMap.get_action_list("delete")[0].get_scancode_with_modifiers(),
- "New Brush": InputMap.get_action_list("new_brush")[0].get_scancode_with_modifiers(),
- "Preferences": 0
- }
+ # Order as in Global.EditMenu enum
+ var edit_menu_items := [
+ "Undo", "Redo", "Copy", "Cut", "Paste", "Delete", "New Brush", "Preferences"
+ ]
var edit_menu: PopupMenu = edit_menu_button.get_popup()
var i := 0
-
- for item in edit_menu_items.keys():
- edit_menu.add_item(item, i, edit_menu_items[item])
+ for item in edit_menu_items:
+ edit_menu.add_item(item, i)
i += 1
edit_menu.set_item_disabled(6, true)
@@ -146,27 +102,26 @@ func _setup_edit_menu() -> void:
func _setup_view_menu() -> void:
- var view_menu_items := { # order as in ViewMenuId enum
- "Tile Mode": 0,
- "Greyscale View": 0,
- "Mirror View": InputMap.get_action_list("mirror_view")[0].get_scancode_with_modifiers(),
- "Show Grid": InputMap.get_action_list("show_grid")[0].get_scancode_with_modifiers(),
- "Show Pixel Grid":
- InputMap.get_action_list("show_pixel_grid")[0].get_scancode_with_modifiers(),
- "Show Rulers": InputMap.get_action_list("show_rulers")[0].get_scancode_with_modifiers(),
- "Show Guides": InputMap.get_action_list("show_guides")[0].get_scancode_with_modifiers(),
- }
+ # Order as in Global.ViewMenu enum
+ var view_menu_items := [
+ "Tile Mode",
+ "Greyscale View",
+ "Mirror View",
+ "Show Grid",
+ "Show Pixel Grid",
+ "Show Rulers",
+ "Show Guides",
+ ]
view_menu = view_menu_button.get_popup()
-
var i := 0
- for item in view_menu_items.keys():
+ for item in view_menu_items:
if item == "Tile Mode":
_setup_tile_mode_submenu(item)
else:
- view_menu.add_check_item(item, i, view_menu_items[item])
+ view_menu.add_check_item(item, i)
i += 1
- view_menu.set_item_checked(ViewMenuId.SHOW_RULERS, true)
- view_menu.set_item_checked(ViewMenuId.SHOW_GUIDES, true)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_RULERS, true)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_GUIDES, true)
view_menu.hide_on_checkable_item_selection = false
view_menu.connect("id_pressed", self, "view_menu_id_pressed")
@@ -186,32 +141,32 @@ func _setup_tile_mode_submenu(item: String) -> void:
func _setup_window_menu() -> void:
- var window_menu_items := { # order as in WindowMenuId enum
- "Window Opacity": 0,
- "Panels": 0,
- "Layouts": 0,
- "Zen Mode": InputMap.get_action_list("zen_mode")[0].get_scancode_with_modifiers(),
- "Fullscreen Mode":
- InputMap.get_action_list("toggle_fullscreen")[0].get_scancode_with_modifiers(),
- }
+ # Order as in Global.WindowMenu enum
+ var window_menu_items := [
+ "Window Opacity",
+ "Panels",
+ "Layouts",
+ "Movable Panels",
+ "Zen Mode",
+ "Fullscreen Mode",
+ ]
window_menu = window_menu_button.get_popup()
-
var i := 0
- for item in window_menu_items.keys():
+ for item in window_menu_items:
if item == "Panels":
_setup_panels_submenu(item)
elif item == "Layouts":
_setup_layouts_submenu(item)
elif item == "Window Opacity":
- window_menu.add_item(item, i, window_menu_items[item])
+ window_menu.add_item(item, i)
else:
- window_menu.add_check_item(item, i, window_menu_items[item])
+ window_menu.add_check_item(item, i)
i += 1
window_menu.hide_on_checkable_item_selection = false
window_menu.connect("id_pressed", self, "window_menu_id_pressed")
# Disable window opacity item if per pixel transparency is not allowed
window_menu.set_item_disabled(
- WindowMenuId.WINDOW_OPACITY,
+ Global.WindowMenu.WINDOW_OPACITY,
!ProjectSettings.get_setting("display/window/per_pixel_transparency/allowed")
)
@@ -219,13 +174,10 @@ func _setup_window_menu() -> void:
func _setup_panels_submenu(item: String) -> void:
panels_submenu.set_name("panels_submenu")
panels_submenu.hide_on_checkable_item_selection = false
- panels_submenu.add_check_item(
- "Moveable Panels", 0, InputMap.get_action_list("edit_mode")[0].get_scancode_with_modifiers()
- )
for element in ui_elements:
panels_submenu.add_check_item(element.name)
var is_hidden: bool = ui.is_control_hidden(element)
- panels_submenu.set_item_checked(ui_elements.find(element) + 1, !is_hidden)
+ panels_submenu.set_item_checked(ui_elements.find(element), !is_hidden)
panels_submenu.connect("id_pressed", self, "_panels_submenu_id_pressed")
window_menu.add_child(panels_submenu)
@@ -264,27 +216,27 @@ func populate_layouts_submenu() -> void:
func _setup_image_menu() -> void:
- var image_menu_items := { # order as in ImageMenuId enum
- "Scale Image": 0,
- "Centralize Image": 0,
- "Crop Image": 0,
- "Resize Canvas": 0,
- "Mirror Image": 0,
- "Rotate Image": 0,
- "Invert Colors": 0,
- "Desaturation": 0,
- "Outline": 0,
- "Drop Shadow": 0,
- "Adjust Hue/Saturation/Value": 0,
- "Gradient": 0,
- # "Shader": 0
- }
+ # Order as in Global.ImageMenu enum
+ var image_menu_items := [
+ "Scale Image",
+ "Centralize Image",
+ "Crop Image",
+ "Resize Canvas",
+ "Mirror Image",
+ "Rotate Image",
+ "Invert Colors",
+ "Desaturation",
+ "Outline",
+ "Drop Shadow",
+ "Adjust Hue/Saturation/Value",
+ "Gradient",
+ # "Shader"
+ ]
var image_menu: PopupMenu = image_menu_button.get_popup()
-
var i := 0
- for item in image_menu_items.keys():
- image_menu.add_item(item, i, image_menu_items[item])
- if i == ImageMenuId.RESIZE_CANVAS:
+ for item in image_menu_items:
+ image_menu.add_item(item, i)
+ if i == Global.ImageMenu.RESIZE_CANVAS:
image_menu.add_separator()
i += 1
@@ -292,35 +244,31 @@ func _setup_image_menu() -> void:
func _setup_select_menu() -> void:
- var select_menu_items := { # order as in EditMenuId enum
- "All": InputMap.get_action_list("select_all")[0].get_scancode_with_modifiers(),
- "Clear": InputMap.get_action_list("clear_selection")[0].get_scancode_with_modifiers(),
- "Invert": InputMap.get_action_list("invert_selection")[0].get_scancode_with_modifiers(),
- }
+ # Order as in Global.SelectMenu enum
+ var select_menu_items := ["All", "Clear", "Invert"]
var select_menu: PopupMenu = select_menu_button.get_popup()
var i := 0
-
- for item in select_menu_items.keys():
- select_menu.add_item(item, i, select_menu_items[item])
+ for item in select_menu_items:
+ select_menu.add_item(item, i)
i += 1
select_menu.connect("id_pressed", self, "select_menu_id_pressed")
func _setup_help_menu() -> void:
- var help_menu_items := { # order as in HelpMenuId enum
- "View Splash Screen": 0,
- "Online Docs": InputMap.get_action_list("open_docs")[0].get_scancode_with_modifiers(),
- "Issue Tracker": 0,
- "Open Logs Folder": 0,
- "Changelog": 0,
- "About Pixelorama": 0
- }
+ # Order as in Global.HelpMenu enum
+ var help_menu_items := [
+ "View Splash Screen",
+ "Online Docs",
+ "Issue Tracker",
+ "Open Logs Folder",
+ "Changelog",
+ "About Pixelorama",
+ ]
var help_menu: PopupMenu = help_menu_button.get_popup()
-
var i := 0
- for item in help_menu_items.keys():
- help_menu.add_item(item, i, help_menu_items[item])
+ for item in help_menu_items:
+ help_menu.add_item(item, i)
i += 1
help_menu.connect("id_pressed", self, "help_menu_id_pressed")
@@ -337,22 +285,22 @@ func _handle_metadata(id: int, menu_button: MenuButton) -> void:
func file_menu_id_pressed(id: int) -> void:
match id:
- FileMenuId.NEW:
+ Global.FileMenu.NEW:
_on_new_project_file_menu_option_pressed()
- FileMenuId.OPEN:
+ Global.FileMenu.OPEN:
_open_project_file()
- FileMenuId.OPEN_LAST_PROJECT:
+ Global.FileMenu.OPEN_LAST_PROJECT:
_on_open_last_project_file_menu_option_pressed()
- FileMenuId.SAVE:
+ Global.FileMenu.SAVE:
_save_project_file()
- FileMenuId.SAVE_AS:
+ Global.FileMenu.SAVE_AS:
_save_project_file_as()
- FileMenuId.EXPORT:
+ Global.FileMenu.EXPORT:
_export_file()
- FileMenuId.EXPORT_AS:
+ Global.FileMenu.EXPORT_AS:
Global.export_dialog.popup_centered()
Global.dialog_open(true)
- FileMenuId.QUIT:
+ Global.FileMenu.QUIT:
Global.control.show_quit_dialog()
_:
_handle_metadata(id, file_menu_button)
@@ -425,21 +373,21 @@ func _on_recent_projects_submenu_id_pressed(id: int) -> void:
func edit_menu_id_pressed(id: int) -> void:
match id:
- EditMenuId.UNDO:
+ Global.EditMenu.UNDO:
Global.current_project.commit_undo()
- EditMenuId.REDO:
+ Global.EditMenu.REDO:
Global.current_project.commit_redo()
- EditMenuId.COPY:
+ Global.EditMenu.COPY:
Global.canvas.selection.copy()
- EditMenuId.CUT:
+ Global.EditMenu.CUT:
Global.canvas.selection.cut()
- EditMenuId.PASTE:
+ Global.EditMenu.PASTE:
Global.canvas.selection.paste()
- EditMenuId.DELETE:
+ Global.EditMenu.DELETE:
Global.canvas.selection.delete()
- EditMenuId.NEW_BRUSH:
+ Global.EditMenu.NEW_BRUSH:
Global.canvas.selection.new_brush()
- EditMenuId.PREFERENCES:
+ Global.EditMenu.PREFERENCES:
Global.preferences_dialog.popup_centered(Vector2(400, 280))
Global.dialog_open(true)
_:
@@ -448,17 +396,17 @@ func edit_menu_id_pressed(id: int) -> void:
func view_menu_id_pressed(id: int) -> void:
match id:
- ViewMenuId.GREYSCALE_VIEW:
+ Global.ViewMenu.GREYSCALE_VIEW:
_toggle_greyscale_view()
- ViewMenuId.MIRROR_VIEW:
+ Global.ViewMenu.MIRROR_VIEW:
_toggle_mirror_view()
- ViewMenuId.SHOW_GRID:
+ Global.ViewMenu.SHOW_GRID:
_toggle_show_grid()
- ViewMenuId.SHOW_PIXEL_GRID:
+ Global.ViewMenu.SHOW_PIXEL_GRID:
_toggle_show_pixel_grid()
- ViewMenuId.SHOW_RULERS:
+ Global.ViewMenu.SHOW_RULERS:
_toggle_show_rulers()
- ViewMenuId.SHOW_GUIDES:
+ Global.ViewMenu.SHOW_GUIDES:
_toggle_show_guides()
_:
_handle_metadata(id, view_menu_button)
@@ -478,26 +426,26 @@ func _tile_mode_submenu_id_pressed(id: int) -> void:
func window_menu_id_pressed(id: int) -> void:
match id:
- WindowMenuId.WINDOW_OPACITY:
+ Global.WindowMenu.WINDOW_OPACITY:
window_opacity_dialog.popup_centered()
Global.dialog_open(true)
- WindowMenuId.ZEN_MODE:
+ Global.WindowMenu.MOVABLE_PANELS:
+ ui.tabs_visible = !ui.tabs_visible
+ window_menu.set_item_checked(id, ui.tabs_visible)
+ Global.WindowMenu.ZEN_MODE:
_toggle_zen_mode()
- WindowMenuId.FULLSCREEN_MODE:
+ Global.WindowMenu.FULLSCREEN_MODE:
_toggle_fullscreen()
_:
_handle_metadata(id, window_menu_button)
func _panels_submenu_id_pressed(id: int) -> void:
- if id == 0:
- ui.tabs_visible = !ui.tabs_visible
- panels_submenu.set_item_checked(0, ui.tabs_visible)
- if zen_mode or id == 0:
+ if zen_mode:
return
var element_visible = panels_submenu.is_item_checked(id)
- ui.set_control_hidden(ui_elements[id - 1], element_visible)
+ ui.set_control_hidden(ui_elements[id], element_visible)
panels_submenu.set_item_checked(id, !element_visible)
@@ -520,12 +468,12 @@ func set_layout(id: int) -> void:
for i in ui_elements.size():
var is_hidden: bool = ui.is_control_hidden(ui_elements[i])
- panels_submenu.set_item_checked(i + 1, !is_hidden)
+ panels_submenu.set_item_checked(i, !is_hidden)
if zen_mode: # Turn zen mode off
Global.control.find_node("TabsContainer").visible = true
zen_mode = false
- window_menu.set_item_checked(WindowMenuId.ZEN_MODE, false)
+ window_menu.set_item_checked(Global.WindowMenu.ZEN_MODE, false)
# Hacky but without 2 idle frames it doesn't work properly. Should be replaced eventually
yield(get_tree(), "idle_frame")
@@ -537,7 +485,7 @@ func set_layout(id: int) -> void:
func _toggle_greyscale_view() -> void:
Global.greyscale_view = !Global.greyscale_view
greyscale_vision.visible = Global.greyscale_view
- view_menu.set_item_checked(ViewMenuId.GREYSCALE_VIEW, Global.greyscale_view)
+ view_menu.set_item_checked(Global.ViewMenu.GREYSCALE_VIEW, Global.greyscale_view)
func _toggle_mirror_view() -> void:
@@ -552,31 +500,31 @@ func _toggle_mirror_view() -> void:
else:
Global.canvas.selection.marching_ants_outline.position.x = 0
Global.canvas.selection.update()
- view_menu.set_item_checked(ViewMenuId.MIRROR_VIEW, Global.mirror_view)
+ view_menu.set_item_checked(Global.ViewMenu.MIRROR_VIEW, Global.mirror_view)
func _toggle_show_grid() -> void:
Global.draw_grid = !Global.draw_grid
- view_menu.set_item_checked(ViewMenuId.SHOW_GRID, Global.draw_grid)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_GRID, Global.draw_grid)
Global.canvas.grid.update()
func _toggle_show_pixel_grid() -> void:
Global.draw_pixel_grid = !Global.draw_pixel_grid
- view_menu.set_item_checked(ViewMenuId.SHOW_PIXEL_GRID, Global.draw_pixel_grid)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_PIXEL_GRID, Global.draw_pixel_grid)
Global.canvas.pixel_grid.update()
func _toggle_show_rulers() -> void:
Global.show_rulers = !Global.show_rulers
- view_menu.set_item_checked(ViewMenuId.SHOW_RULERS, Global.show_rulers)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_RULERS, Global.show_rulers)
Global.horizontal_ruler.visible = Global.show_rulers
Global.vertical_ruler.visible = Global.show_rulers
func _toggle_show_guides() -> void:
Global.show_guides = !Global.show_guides
- view_menu.set_item_checked(ViewMenuId.SHOW_GUIDES, Global.show_guides)
+ view_menu.set_item_checked(Global.ViewMenu.SHOW_GUIDES, Global.show_guides)
for guide in Global.canvas.get_children():
if guide is Guide and guide in Global.current_project.guides:
guide.visible = Global.show_guides
@@ -591,64 +539,64 @@ func _toggle_zen_mode() -> void:
for i in ui_elements.size():
if ui_elements[i].name == "Main Canvas":
continue
- if !panels_submenu.is_item_checked(i + 1):
+ if !panels_submenu.is_item_checked(i):
continue
ui.set_control_hidden(ui_elements[i], !zen_mode)
Global.control.find_node("TabsContainer").visible = zen_mode
zen_mode = !zen_mode
- window_menu.set_item_checked(WindowMenuId.ZEN_MODE, zen_mode)
+ window_menu.set_item_checked(Global.WindowMenu.ZEN_MODE, zen_mode)
func _toggle_fullscreen() -> void:
OS.window_fullscreen = !OS.window_fullscreen
- window_menu.set_item_checked(WindowMenuId.FULLSCREEN_MODE, OS.window_fullscreen)
+ window_menu.set_item_checked(Global.WindowMenu.FULLSCREEN_MODE, OS.window_fullscreen)
if OS.window_fullscreen: # If window is fullscreen then reset transparency
window_opacity_dialog.set_window_opacity(1.0)
func image_menu_id_pressed(id: int) -> void:
match id:
- ImageMenuId.SCALE_IMAGE:
+ Global.ImageMenu.SCALE_IMAGE:
_show_scale_image_popup()
- ImageMenuId.CENTRALIZE_IMAGE:
+ Global.ImageMenu.CENTRALIZE_IMAGE:
DrawingAlgos.centralize()
- ImageMenuId.CROP_IMAGE:
+ Global.ImageMenu.CROP_IMAGE:
DrawingAlgos.crop_image()
- ImageMenuId.RESIZE_CANVAS:
+ Global.ImageMenu.RESIZE_CANVAS:
_show_resize_canvas_popup()
- ImageMenuId.FLIP:
+ Global.ImageMenu.FLIP:
Global.control.get_node("Dialogs/ImageEffects/FlipImageDialog").popup_centered()
Global.dialog_open(true)
- ImageMenuId.ROTATE:
+ Global.ImageMenu.ROTATE:
_show_rotate_image_popup()
- ImageMenuId.INVERT_COLORS:
+ Global.ImageMenu.INVERT_COLORS:
Global.control.get_node("Dialogs/ImageEffects/InvertColorsDialog").popup_centered()
Global.dialog_open(true)
- ImageMenuId.DESATURATION:
+ Global.ImageMenu.DESATURATION:
Global.control.get_node("Dialogs/ImageEffects/DesaturateDialog").popup_centered()
Global.dialog_open(true)
- ImageMenuId.OUTLINE:
+ Global.ImageMenu.OUTLINE:
_show_add_outline_popup()
- ImageMenuId.DROP_SHADOW:
+ Global.ImageMenu.DROP_SHADOW:
_show_drop_shadow_popup()
- ImageMenuId.HSV:
+ Global.ImageMenu.HSV:
_show_hsv_configuration_popup()
- ImageMenuId.GRADIENT:
+ Global.ImageMenu.GRADIENT:
Global.control.get_node("Dialogs/ImageEffects/GradientDialog").popup_centered()
Global.dialog_open(true)
-# ImageMenuId.SHADER:
+# Global.ImageMenu.SHADER:
# Global.control.get_node("Dialogs/ImageEffects/ShaderEffect").popup_centered()
# Global.dialog_open(true)
@@ -688,11 +636,11 @@ func _show_hsv_configuration_popup() -> void:
func select_menu_id_pressed(id: int) -> void:
match id:
- SelectMenuId.SELECT_ALL:
+ Global.SelectMenu.SELECT_ALL:
Global.canvas.selection.select_all()
- SelectMenuId.CLEAR_SELECTION:
+ Global.SelectMenu.CLEAR_SELECTION:
Global.canvas.selection.clear_selection(true)
- SelectMenuId.INVERT:
+ Global.SelectMenu.INVERT:
Global.canvas.selection.invert()
_:
_handle_metadata(id, select_menu_button)
@@ -700,22 +648,22 @@ func select_menu_id_pressed(id: int) -> void:
func help_menu_id_pressed(id: int) -> void:
match id:
- HelpMenuId.VIEW_SPLASH_SCREEN:
+ Global.HelpMenu.VIEW_SPLASH_SCREEN:
Global.control.get_node("Dialogs/SplashDialog").popup_centered()
Global.dialog_open(true)
- HelpMenuId.ONLINE_DOCS:
+ Global.HelpMenu.ONLINE_DOCS:
OS.shell_open("https://orama-interactive.github.io/Pixelorama-Docs/")
- HelpMenuId.ISSUE_TRACKER:
+ Global.HelpMenu.ISSUE_TRACKER:
OS.shell_open("https://github.com/Orama-Interactive/Pixelorama/issues")
- HelpMenuId.OPEN_LOGS_FOLDER:
+ Global.HelpMenu.OPEN_LOGS_FOLDER:
var dir = Directory.new()
dir.make_dir_recursive("user://logs") # In case someone deleted it
OS.shell_open(ProjectSettings.globalize_path("user://logs"))
- HelpMenuId.CHANGELOG:
+ Global.HelpMenu.CHANGELOG:
OS.shell_open(
"https://github.com/Orama-Interactive/Pixelorama/blob/master/CHANGELOG.md#v010---2022-04-15"
)
- HelpMenuId.ABOUT_PIXELORAMA:
+ Global.HelpMenu.ABOUT_PIXELORAMA:
Global.control.get_node("Dialogs/AboutDialog").popup_centered()
Global.dialog_open(true)
_:
diff --git a/src/UI/UI.tscn b/src/UI/UI.tscn
index 702a09cbb..d9cdf0e5d 100644
--- a/src/UI/UI.tscn
+++ b/src/UI/UI.tscn
@@ -285,6 +285,7 @@ script = ExtResource( 23 )
[node name="Viewport" type="Viewport" parent="DockableContainer/Main Canvas/ViewportandVerticalRuler/ViewportContainer"]
size = Vector2( 862, 466 )
+handle_input_locally = false
usage = 0
render_target_update_mode = 3