Implement the Keychain Plugin (#700)
* Start implementing the godot_better_input plugin * Update ShortcutEdit.gd * Load & save preset option * Add some groups and fix action events not being deleted on load * Add MenuInputAction class for multiple menu accelerators * Create a proper plugin and a BetterInput autoload * Update menu accelerators * Move settings to BetterInput * Move menu enums to Global, make more MenuInputActions * Add more menu events * Add new groups * Optimize BetterInput _input() method * Remove a lot of lines of code * Change some previous events, add ignore actions and a View menu group * Change update_item_accelerator to update_ui * Move MenuInputAction initialization to BetterInput.gd * Update hint tooltips when a shortcut changes Temporarily comment out some code regarding the configurable modifiers * Some MenuInputAction variable name changes * Add handle_input() to InputAction * Update the shortcuts of buttons * Fix shortcut selector menu position * Change plugin name into Keychain * Fix keyboard input dialog exiting when Enter or Space is being pressed * Add two more groups * Make groups folded by default * Temporarily make tool modifier shortcuts not configurable A temporary change, they will be made configurable again, with different actions that are currently mapped to the same events, local/independent from each other. * Add license for Keychain * Fix issue where a key event would be added in other input types * Fix bug where the assigned state was not updated when the dialog appeared again * Update Main.tscn * Add a disabled line edit in keyboard shortcut selector to grab focus * Load presets in the Keychain autoload This way, the input actions get updated from the start, instead of only at the ShortcutEdit scene. WARNING, this currently causes crashes if the menu items have no shortcut binded to them. * Move custom settings away from Keychain.gd To keep it the same as the upstream plugin * Change menu enum names * Made action_get_first_key() more general * Use arrays for menu items instead of dictionaries, fixes crash * Move moveable panels to Window menu * Format * Optimize hint tooltip updating * Add support for translations in Keychain * Translation changes * Made tool modifiers configurable Needs more testing. * Made camera arrow key movement configurable & joypad axis support This commit removes the ability to press Shift and Control+Shift to adjust the camera arrow key movement speed. Instead, the speed depends on the zoom level. The right joypad analog stick is configured to move the camera by default. * Rename presets into shortcut profiles, use Resources and let users create their own * [skip ci] Update addons README * Update Global.gd
|
@ -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
|
||||
|
|
216
addons/keychain/Keychain.gd
Normal file
|
@ -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
|
21
addons/keychain/LICENSE
Normal file
|
@ -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.
|
410
addons/keychain/ShortcutEdit.gd
Normal file
|
@ -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)
|
114
addons/keychain/ShortcutEdit.tscn
Normal file
|
@ -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"]
|
31
addons/keychain/ShortcutProfile.gd
Normal file
|
@ -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
|
190
addons/keychain/ShortcutSelectorDialog.gd
Normal file
|
@ -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)
|
49
addons/keychain/ShortcutSelectorDialog.tscn
Normal file
|
@ -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"]
|
1
addons/keychain/assets/add.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1v6h-6v2h6v6h2v-6h6v-2h-6v-6z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 149 B |
35
addons/keychain/assets/add.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/close.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m3.7578 2.3438-1.4141 1.4141 4.2422 4.2422-4.2422 4.2422 1.4141 1.4141 4.2422-4.2422 4.2422 4.2422 1.4141-1.4141-4.2422-4.2422 4.2422-4.2422-1.4141-1.4141-4.2422 4.2422z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 286 B |
35
addons/keychain/assets/close.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/edit.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1c-.554 0-1 .446-1 1v2h4v-2c0-.554-.446-1-1-1zm-1 4v7l2 3 2-3v-7zm1 1h1v5h-1z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 197 B |
35
addons/keychain/assets/edit.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/folder.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2a1 1 0 0 0 -1 1v2 6 2a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-7a1 1 0 0 0 -1-1h-4a1 1 0 0 1 -1-1v-1a1 1 0 0 0 -1-1z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 228 B |
35
addons/keychain/assets/folder.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/joy_axis.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g transform="translate(0 -1036.4)"><path d="m27 1038.4h7v14h-7z" fill="#fff" fill-opacity=".99608"/><g fill="#e0e0e0"><path d="m3 1a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h12v-14zm4 2h2a1 1 0 0 1 1 1v2h2a1 1 0 0 1 1 1v2a1 1 0 0 1 -1 1h-2v2a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1-1v-2h-2a1 1 0 0 1 -1-1v-2a1 1 0 0 1 1-1h2v-2a1 1 0 0 1 1-1z" fill-opacity=".99608" transform="translate(0 1036.4)"/><circle cx="8" cy="1044.4" r="1"/></g></g></svg>
|
After Width: | Height: | Size: 512 B |
35
addons/keychain/assets/joy_axis.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/joy_button.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".99608" transform="translate(0 -1036.4)"><path d="m27 1038.4h7v14h-7z" fill="#fff"/><path d="m1 1v14h12c1.1046 0 2-.8954 2-2v-10c0-1.1046-.89543-2-2-2zm7 1a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2zm-4 4a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2zm8 0a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2zm-4 4a2 2 0 0 1 2 2 2 2 0 0 1 -2 2 2 2 0 0 1 -2-2 2 2 0 0 1 2-2z" fill="#e0e0e0" transform="translate(0 1036.4)"/></g></svg>
|
After Width: | Height: | Size: 566 B |
35
addons/keychain/assets/joy_button.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/keyboard.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm1.543 1.139h1.393l1.834 4.199h1.295v.437c.708.052 1.246.239 1.61.559.368.316.55.747.55 1.295 0 .552-.182.99-.55 1.314-.368.32-.906.505-1.61.553v.467h-1.294v-.473c-.708-.06-1.247-.248-1.615-.564-.364-.316-.545-.75-.545-1.297 0-.548.181-.977.545-1.29.368-.315.907-.504 1.615-.564v-.437h-1.464l-.282-.733h-1.595l-.284.733h-1.439l1.836-4.2zm.684 1.39-.409 1.057h.817zm3.84 4.338v1.526c.28-.04.483-.12.607-.24.124-.125.185-.302.185-.53 0-.224-.063-.396-.191-.516-.124-.12-.326-.2-.602-.24zm-1.296.006c-.284.04-.487.12-.61.24-.12.116-.182.288-.182.516 0 .22.065.392.193.512.132.12.331.202.6.246v-1.514z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg>
|
After Width: | Height: | Size: 961 B |
35
addons/keychain/assets/keyboard.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/keyboard_physical.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill-opacity=".996"><path d="m4 2a1 1 0 0 0 -1 1v9.084c0 .506.448.916 1 .916h8c.552 0 1-.41 1-.916v-9.084a1 1 0 0 0 -1-1zm2.762 1.768h2.476l3.264 7.464h-2.604l-.502-1.3h-2.835l-.502 1.3h-2.561zm1.217 2.474-.725 1.878h1.45z" fill="#e0e0e0"/><path d="m27 2h7v14h-7z" fill="#fff"/><path d="m1 4v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a1 1 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9z" fill="#e0e0e0"/></g></svg>
|
After Width: | Height: | Size: 465 B |
35
addons/keychain/assets/keyboard_physical.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/mouse.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7 1.1016a5 5 0 0 0 -4 4.8984h4zm2 .0039063v4.8945h4a5 5 0 0 0 -4-4.8945zm-6 6.8945v2a5 5 0 0 0 5 5 5 5 0 0 0 5-5v-2z" fill="#e0e0e0"/></svg>
|
After Width: | Height: | Size: 234 B |
35
addons/keychain/assets/mouse.svg.import
Normal file
|
@ -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
|
1
addons/keychain/assets/shortcut.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m4 2c-.55228 0-1 .4477-1 1v9.084c.0004015.506.448.91602 1 .91602h8c.552 0 .9996-.41002 1-.91602v-9.084c0-.5523-.44772-1-1-1zm-3 2v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-9h-1v9a.99998.99998 0 0 1 -1 1h-10a1 1 0 0 1 -1-1v-9zm6 0h3l-1 3h2l-4 4 1-3h-2z" fill="#e0e0e0" fill-opacity=".99608"/></svg>
|
After Width: | Height: | Size: 382 B |
35
addons/keychain/assets/shortcut.svg.import
Normal file
|
@ -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
|
7
addons/keychain/plugin.cfg
Normal file
|
@ -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"
|
10
addons/keychain/plugin.gd
Normal file
|
@ -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")
|
10
addons/keychain/profiles/default.tres
Normal file
|
@ -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 = {
|
||||
}
|
4
addons/keychain/translations/README.md
Normal file
|
@ -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.
|
209
addons/keychain/translations/Translations.pot
Normal file
|
@ -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 ""
|
215
addons/keychain/translations/el_GR.po
Normal file
|
@ -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)"
|
126
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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
20
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(
|
||||
|
|
|
@ -26,7 +26,6 @@ theme = ExtResource( 1 )
|
|||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_horizontal_guides_": [ ],
|
||||
"_edit_use_anchors_": false,
|
||||
"_edit_vertical_guides_": [ ]
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
_:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|