diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index b035353..88e526d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,7 +11,7 @@ env:
# Which godot version to use for exporting.
GODOT_VERSION: 4.4.1
# Which godot release to use for exporting. (stable/rc/beta/alpha)
- GODOT_RELEASE: rc1
+ GODOT_RELEASE: stable
# Used in the editor config file name. Do not change this for patch releases.
GODOT_FEATURE_VERSION: 4.4
# Commit hash
diff --git a/assets/icons/Quit.svg b/assets/icons/Quit.svg
new file mode 100644
index 0000000..ee8b477
--- /dev/null
+++ b/assets/icons/Quit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/icons/Quit.svg.import b/assets/icons/Quit.svg.import
new file mode 100644
index 0000000..c7fefa3
--- /dev/null
+++ b/assets/icons/Quit.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cbkrorp0b7qgb"
+path="res://.godot/imported/Quit.svg-27ce495cfd421ac10bdab78eb7fc0164.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/icons/Quit.svg"
+dest_files=["res://.godot/imported/Quit.svg-27ce495cfd421ac10bdab78eb7fc0164.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/assets/icons/Snap.svg b/assets/icons/Snap.svg
index 3e13d62..8edc101 100644
--- a/assets/icons/Snap.svg
+++ b/assets/icons/Snap.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/godot_only/icons/BetterButton.svg b/godot_only/icons/BetterButton.svg
new file mode 100644
index 0000000..e789cb4
--- /dev/null
+++ b/godot_only/icons/BetterButton.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/godot_only/icons/BetterButton.svg.import b/godot_only/icons/BetterButton.svg.import
new file mode 100644
index 0000000..8591340
--- /dev/null
+++ b/godot_only/icons/BetterButton.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dvqpk5nveft8r"
+path="res://.godot/imported/BetterButton.svg-05d13469c50d2eb36ca24ebd664433ac.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://godot_only/icons/BetterButton.svg"
+dest_files=["res://.godot/imported/BetterButton.svg-05d13469c50d2eb36ca24ebd664433ac.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/project.godot b/project.godot
index 6a85d07..3bf64ac 100644
--- a/project.godot
+++ b/project.godot
@@ -159,17 +159,6 @@ move_down={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":false,"pressed":false,"keycode":4194322,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
-undo={
-"deadzone": 0.2,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":false,"pressed":false,"keycode":90,"physical_keycode":0,"key_label":0,"unicode":122,"location":0,"echo":false,"script":null)
-]
-}
-redo={
-"deadzone": 0.2,
-"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":true,"pressed":false,"keycode":90,"physical_keycode":0,"key_label":0,"unicode":90,"location":0,"echo":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":false,"pressed":false,"keycode":89,"physical_keycode":0,"key_label":0,"unicode":121,"location":0,"echo":false,"script":null)
-]
-}
duplicate={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"command_or_control_autoremap":true,"alt_pressed":false,"shift_pressed":false,"pressed":false,"keycode":68,"physical_keycode":0,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
diff --git a/src/autoload/Configs.gd b/src/autoload/Configs.gd
index 010e37f..8ced14a 100644
--- a/src/autoload/Configs.gd
+++ b/src/autoload/Configs.gd
@@ -18,10 +18,14 @@ signal basic_colors_changed
@warning_ignore("unused_signal")
signal handle_visuals_changed
@warning_ignore("unused_signal")
+signal grid_color_changed
+@warning_ignore("unused_signal")
signal shortcut_panel_changed
@warning_ignore("unused_signal")
signal active_tab_status_changed
@warning_ignore("unused_signal")
+signal active_tab_reference_changed
+@warning_ignore("unused_signal")
signal active_tab_changed
@warning_ignore("unused_signal")
signal tabs_changed
@@ -43,7 +47,7 @@ var default_shortcuts: Dictionary[String, Array] = {}
func _enter_tree() -> void:
# Fill up the default shortcuts dictionary before the shortcuts are loaded.
- for action in ShortcutUtils.get_all_shortcuts():
+ for action in ShortcutUtils.get_all_actions():
if InputMap.has_action(action):
default_shortcuts[action] = InputMap.action_get_events(action)
load_config()
@@ -69,7 +73,7 @@ func reset_settings() -> void:
savedata = SaveData.new()
savedata.reset_to_default()
savedata.language = "en"
- savedata.set_shortcut_panel_slots({ 0: "undo", 1: "redo", 2: "save"})
+ savedata.set_shortcut_panel_slots({ 0: "ui_undo", 1: "ui_redo" })
savedata.set_palettes([Palette.new("Pure", Palette.Preset.PURE)])
save()
diff --git a/src/autoload/HandlerGUI.gd b/src/autoload/HandlerGUI.gd
index 2f9b175..05dd393 100644
--- a/src/autoload/HandlerGUI.gd
+++ b/src/autoload/HandlerGUI.gd
@@ -196,14 +196,6 @@ func _parse_popup_overlay_event(event: InputEvent) -> void:
var last_mouse_click_double := false
func _input(event: InputEvent) -> void:
- if ShortcutUtils.is_action_pressed(event, "quit"):
- remove_all_menus()
- var confirm_dialog := ConfirmDialogScene.instantiate()
- add_menu(confirm_dialog)
- confirm_dialog.setup(Translator.translate("Quit GodSVG"),
- Translator.translate("Do you want to quit GodSVG?"),
- Translator.translate("Quit"), get_tree().quit)
-
# So, it turns out that when you double click, only the press will count as such.
# I don't like that, and it causes problems! So mark the release as double_click too.
# TODO Godot PR #92582 fixes this.
@@ -213,63 +205,142 @@ func _input(event: InputEvent) -> void:
elif last_mouse_click_double and event.is_released():
event.double_click = true
last_mouse_click_double = false
-
- # Stuff that should replace the existing overlays, or that opens separate windows.
- const CONST_ARR_1: PackedStringArray = ["about_info", "about_donate", "check_updates",
- "open_settings", "open_externally", "open_in_folder"]
- for action in CONST_ARR_1:
- if ShortcutUtils.is_action_pressed(event, action):
- remove_all_menus()
+
+
+func _unhandled_input(event: InputEvent) -> void:
+ # Clear popups or overlays.
+ if ShortcutUtils.is_action_pressed(event, "ui_cancel"):
+ if not popup_stack.is_empty():
+ get_viewport().set_input_as_handled()
+ remove_popup()
+ return
+ elif not menu_stack.is_empty():
get_viewport().set_input_as_handled()
- ShortcutUtils.fn_call(action)
+ _remove_control()
return
- # Stuff that links externally.
- const CONST_ARR_2: PackedStringArray = ["about_repo", "about_website"]
- for action in CONST_ARR_2:
+ for action in ShortcutUtils.UNIVERSAL_ACTIONS:
if ShortcutUtils.is_action_pressed(event, action):
- get_viewport().set_input_as_handled()
- ShortcutUtils.fn_call(action)
+ match action:
+ "quit": prompt_quit()
+ "about_info": open_about()
+ "about_donate": open_donate()
+ "check_updates": open_update_checker()
+ "open_settings": open_settings()
+ "about_repo": OS.shell_open("https://github.com/syntaxerror247/GodSVG-Mobile")
+ "about_website": OS.shell_open("https://godsvg.com")
+ "open_externally": FileUtils.open_svg(
+ Configs.savedata.get_active_tab().svg_file_path)
+ "open_in_folder": FileUtils.open_svg_folder(
+ Configs.savedata.get_active_tab().svg_file_path)
return
- # Stop the logic below from running if there's overlays.
- if not popup_stack.is_empty() or not menu_stack.is_empty():
+ # Stop the logic below from running if there's menu overlays.
+ if not menu_stack.is_empty():
return
- # Global actions that should happen regardless of the context.
- const CONST_ARR_3: PackedStringArray = ["import", "export", "save", "save_as",
- "close_tab", "close_tabs_to_left", "close_tabs_to_right", "close_all_other_tabs",
- "new_tab", "select_next_tab", "select_previous_tab", "copy_svg_text", "optimize",
- "reset_svg"]
- for action in CONST_ARR_3:
+ for action in ShortcutUtils.EFFECT_ACTIONS:
if ShortcutUtils.is_action_pressed(event, action):
- get_viewport().set_input_as_handled()
- ShortcutUtils.fn_call(action)
-
-func _unhandled_input(event: InputEvent) -> void:
- # Clear popups or overlays.
- if not popup_stack.is_empty() and event.is_action_pressed("ui_cancel"):
- get_viewport().set_input_as_handled()
- remove_popup()
- return
- elif not menu_stack.is_empty() and event.is_action_pressed("ui_cancel"):
- get_viewport().set_input_as_handled()
- _remove_control()
+ match action:
+ "view_show_grid": State.toggle_show_grid()
+ "view_show_handles": State.toggle_show_handles()
+ "view_rasterized_svg": State.toggle_view_rasterized()
+ "view_show_reference": State.toggle_show_reference()
+ "view_overlay_reference": State.toggle_overlay_reference()
+ "load_reference": FileUtils.open_image_import_dialog()
+ "toggle_snap": Configs.savedata.snap *= -1
+
+ if not popup_stack.is_empty():
return
- if not popup_stack.is_empty() or not menu_stack.is_empty() or\
- get_viewport().gui_is_dragging():
+ # Global actions that should happen regardless of the context.
+ for action in ShortcutUtils.EDITOR_ACTIONS:
+ if ShortcutUtils.is_action_pressed(event, action):
+ match action:
+ "import": FileUtils.open_svg_import_dialog()
+ "export": open_export()
+ "save": FileUtils.save_svg()
+ "save_as": FileUtils.save_svg_as()
+ "close_tab": FileUtils.close_tabs(Configs.savedata.get_active_tab_index())
+ "close_tabs_to_left": FileUtils.close_tabs(
+ Configs.savedata.get_active_tab_index(),
+ FileUtils.TabCloseMode.TO_LEFT)
+ "close_tabs_to_right": FileUtils.close_tabs(
+ Configs.savedata.get_active_tab_index(),
+ FileUtils.TabCloseMode.TO_RIGHT)
+ "close_all_other_tabs": FileUtils.close_tabs(
+ Configs.savedata.get_active_tab_index(),
+ FileUtils.TabCloseMode.ALL_OTHERS)
+ "new_tab": Configs.savedata.add_empty_tab()
+ "select_next_tab": Configs.savedata.set_active_tab_index(
+ posmod(Configs.savedata.get_active_tab_index() + 1,
+ Configs.savedata.get_tab_count()))
+ "select_previous_tab": Configs.savedata.set_active_tab_index(
+ posmod(Configs.savedata.get_active_tab_index() - 1,
+ Configs.savedata.get_tab_count()))
+ "copy_svg_text": DisplayServer.clipboard_set(State.svg_text)
+ "optimize": State.optimize()
+ "reset_svg": FileUtils.reset_svg()
+ "debug": State.toggle_show_debug()
+ return
+
+ # Stop the logic below from running while GUI dragging is going on.
+ if get_viewport().gui_is_dragging():
return
- const CONST_ARR: PackedStringArray = ["redo", "undo", "ui_cancel", "delete", "move_up",
- "move_down", "duplicate", "select_all"]
- for action in CONST_ARR:
+ for action in ShortcutUtils.PRISTINE_ACTIONS:
if ShortcutUtils.is_action_pressed(event, action):
- get_viewport().set_input_as_handled()
- ShortcutUtils.fn_call(action)
+ match action:
+ "ui_undo": Configs.savedata.get_active_tab().undo()
+ "ui_redo": Configs.savedata.get_active_tab().redo()
+ "ui_cancel": State.clear_all_selections()
+ "delete": State.delete_selected()
+ "move_up": State.move_up_selected()
+ "move_down": State.move_down_selected()
+ "duplicate": State.duplicate_selected()
+ "select_all": State.select_all()
return
- if event is InputEventKey:
- State.respond_to_key_input(event)
+
+ if ShortcutUtils.is_action_pressed(event, "move_absolute"):
+ State.respond_to_key_input("M")
+ elif ShortcutUtils.is_action_pressed(event, "move_relative"):
+ State.respond_to_key_input("m")
+ elif ShortcutUtils.is_action_pressed(event, "line_absolute"):
+ State.respond_to_key_input("L")
+ elif ShortcutUtils.is_action_pressed(event, "line_relative"):
+ State.respond_to_key_input("l")
+ elif ShortcutUtils.is_action_pressed(event, "horizontal_line_absolute"):
+ State.respond_to_key_input("H")
+ elif ShortcutUtils.is_action_pressed(event, "horizontal_line_relative"):
+ State.respond_to_key_input("h")
+ elif ShortcutUtils.is_action_pressed(event, "vertical_line_absolute"):
+ State.respond_to_key_input("V")
+ elif ShortcutUtils.is_action_pressed(event, "vertical_line_relative"):
+ State.respond_to_key_input("v")
+ elif ShortcutUtils.is_action_pressed(event, "close_path_absolute"):
+ State.respond_to_key_input("Z")
+ elif ShortcutUtils.is_action_pressed(event, "close_path_relative"):
+ State.respond_to_key_input("z")
+ elif ShortcutUtils.is_action_pressed(event, "elliptical_arc_absolute"):
+ State.respond_to_key_input("A")
+ elif ShortcutUtils.is_action_pressed(event, "elliptical_arc_relative"):
+ State.respond_to_key_input("a")
+ elif ShortcutUtils.is_action_pressed(event, "cubic_bezier_absolute"):
+ State.respond_to_key_input("C")
+ elif ShortcutUtils.is_action_pressed(event, "cubic_bezier_relative"):
+ State.respond_to_key_input("c")
+ elif ShortcutUtils.is_action_pressed(event, "shorthand_cubic_bezier_absolute"):
+ State.respond_to_key_input("S")
+ elif ShortcutUtils.is_action_pressed(event, "shorthand_cubic_bezier_relative"):
+ State.respond_to_key_input("s")
+ elif ShortcutUtils.is_action_pressed(event, "quadratic_bezier_absolute"):
+ State.respond_to_key_input("Q")
+ elif ShortcutUtils.is_action_pressed(event, "quadratic_bezier_relative"):
+ State.respond_to_key_input("q")
+ elif ShortcutUtils.is_action_pressed(event, "shorthand_quadratic_bezier_absolute"):
+ State.respond_to_key_input("T")
+ elif ShortcutUtils.is_action_pressed(event, "shorthand_quadratic_bezier_relative"):
+ State.respond_to_key_input("t")
func get_window_default_size() -> Vector2i:
@@ -322,7 +393,16 @@ func update_ui_scale() -> void:
window.content_scale_factor = final_scale
+func prompt_quit() -> void:
+ remove_all_menus()
+ var confirm_dialog := ConfirmDialogScene.instantiate()
+ add_menu(confirm_dialog)
+ confirm_dialog.setup(Translator.translate("Quit GodSVG"),
+ Translator.translate("Do you want to quit GodSVG?"),
+ Translator.translate("Quit"), get_tree().quit)
+
func open_update_checker() -> void:
+ remove_all_menus()
var confirmation_dialog := ConfirmDialogScene.instantiate()
add_menu(confirmation_dialog)
confirmation_dialog.setup(Translator.translate("Check for updates?"),
@@ -335,15 +415,19 @@ func _list_updates() -> void:
add_menu(update_menu_instance)
func open_settings() -> void:
+ remove_all_menus()
add_menu(SettingsMenuScene.instantiate())
func open_about() -> void:
+ remove_all_menus()
add_menu(AboutMenuScene.instantiate())
func open_donate() -> void:
+ remove_all_menus()
add_menu(DonateMenuScene.instantiate())
func open_export() -> void:
+ remove_all_menus()
var width := State.root_element.width
var height := State.root_element.height
@@ -409,3 +493,28 @@ func throw_mouse_motion_event() -> void:
# Must multiply by the final transform because the InputEvent is not yet parsed.
mm_event.position = window.get_mouse_position() * window.get_final_transform()
Input.parse_input_event.call_deferred(mm_event)
+
+# Trigger a shortcut automatically.
+func throw_action_event(action: String) -> void:
+ var events := InputMap.action_get_events(action)
+ for event in events:
+ if ShortcutUtils.is_shortcut_valid(event, action):
+ # Pressed keys.
+ var press_key_event := event.duplicate()
+ press_key_event.pressed = true
+ Input.parse_input_event(press_key_event)
+ # Released keys.
+ var release_key_event := press_key_event.duplicate()
+ release_key_event.pressed = false
+ Input.parse_input_event(release_key_event)
+ return
+
+ # Pressed action.
+ var press_action_event := InputEventAction.new()
+ press_action_event.action = action
+ press_action_event.pressed = true
+ Input.parse_input_event.call_deferred(press_action_event)
+ # Released action.
+ var release_action_event := InputEventAction.new()
+ release_action_event.action = action
+ Input.parse_input_event.call_deferred(release_action_event)
diff --git a/src/autoload/State.gd b/src/autoload/State.gd
index df73122..42327b9 100644
--- a/src/autoload/State.gd
+++ b/src/autoload/State.gd
@@ -4,19 +4,6 @@ extends Node
const OptionsDialogScene = preload("res://src/ui_widgets/options_dialog.tscn")
const PathCommandPopupScene = preload("res://src/ui_widgets/path_popup.tscn")
-const path_actions_dict: Dictionary[String, String] = {
- "move_absolute": "M", "move_relative": "m",
- "line_absolute": "L", "line_relative": "l",
- "horizontal_line_absolute": "H", "horizontal_line_relative": "h",
- "vertical_line_absolute": "V", "vertical_line_relative": "v",
- "close_path_absolute": "Z", "close_path_relative": "z",
- "elliptical_arc_absolute": "A", "elliptical_arc_relative": "a",
- "cubic_bezier_absolute": "C", "cubic_bezier_relative": "c",
- "shorthand_cubic_bezier_absolute": "S", "shorthand_cubic_bezier_relative": "s",
- "quadratic_bezier_absolute": "Q", "quadratic_bezier_relative": "q",
- "shorthand_quadratic_bezier_absolute": "T", "shorthand_quadratic_bezier_relative": "t"
-}
-
signal svg_unknown_change
signal svg_resized
@@ -78,11 +65,22 @@ func _enter_tree() -> void:
setup_from_tab.call_deferred() # Let everything load before emitting signals.
var cmdline_args := OS.get_cmdline_args()
- if not (OS.is_debug_build() and not OS.has_feature("template")) and\
- cmdline_args.size() >= 1:
- await get_tree().ready # Ensures we can add warning panels.
- FileUtils.apply_svg_from_path(cmdline_args[0])
-
+
+ # The first argument passed is always a path to the scene file when in-editor.
+ if (OS.is_debug_build() and not OS.has_feature("template")) and cmdline_args.size() >= 1:
+ cmdline_args.remove_at(0)
+
+ if cmdline_args.size() >= 1:
+ # Need to wait a frame so the import warnings panel can be added.
+ await get_tree().process_frame
+
+ var used_tab_paths := PackedStringArray()
+ for tab in Configs.savedata.get_tabs():
+ used_tab_paths.append(tab.svg_file_path)
+
+ for path in cmdline_args:
+ if path.get_extension() == "svg" and not path in used_tab_paths:
+ FileUtils.apply_svg_from_path(path)
func setup_from_tab() -> void:
var active_tab := Configs.savedata.get_active_tab()
@@ -265,25 +263,40 @@ func set_viewport_size(new_value: Vector2i) -> void:
var view_rasterized := false
var show_grid := true
var show_handles := true
+var show_reference := false
+var overlay_reference := false
+var show_debug := false
signal view_rasterized_changed
signal show_grid_changed
signal show_handles_changed
+signal show_reference_changed
+signal overlay_reference_changed
+signal show_debug_changed
-func set_view_rasterized(new_value: bool) -> void:
- if view_rasterized != new_value:
- view_rasterized = new_value
- view_rasterized_changed.emit()
+func toggle_view_rasterized() -> void:
+ view_rasterized = not view_rasterized
+ view_rasterized_changed.emit()
-func set_show_grid(new_value: bool) -> void:
- if show_grid != new_value:
- show_grid = new_value
- show_grid_changed.emit()
+func toggle_show_grid() -> void:
+ show_grid = not show_grid
+ show_grid_changed.emit()
-func set_show_handles(new_value: bool) -> void:
- if show_handles != new_value:
- show_handles = new_value
- show_handles_changed.emit()
+func toggle_show_handles() -> void:
+ show_handles = not show_handles
+ show_handles_changed.emit()
+
+func toggle_show_reference() -> void:
+ show_reference = not show_reference
+ show_reference_changed.emit()
+
+func toggle_overlay_reference() -> void:
+ overlay_reference = not overlay_reference
+ overlay_reference_changed.emit()
+
+func toggle_show_debug() -> void:
+ show_debug = not show_debug
+ show_debug_changed.emit()
# Override the selected elements with a single new selected element.
@@ -514,13 +527,13 @@ func is_hovered(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> b
if inner_idx == -1:
return XIDUtils.is_parent_or_self(hovered_xid, xid)
else:
- return XIDUtils.is_parent_or_self(hovered_xid, xid) or\
- (semi_hovered_xid == xid and inner_hovered == inner_idx)
+ return (inner_hovered == inner_idx and semi_hovered_xid == xid) or\
+ XIDUtils.is_parent_or_self(hovered_xid, xid)
else:
if inner_idx == -1:
return hovered_xid == xid
else:
- return semi_hovered_xid == xid and inner_hovered == inner_idx
+ return inner_hovered == inner_idx and semi_hovered_xid == xid
# Returns whether the given element or inner editor is selected.
func is_selected(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> bool:
@@ -619,47 +632,38 @@ func _on_xnodes_moved_to(xids: Array[PackedInt32Array], location: PackedInt32Arr
selection_changed.emit()
-func respond_to_key_input(event: InputEventKey) -> void:
- # Path commands using keys.
- if inner_selections.is_empty() or event.is_command_or_control_pressed():
+# Path commands using keys.
+func respond_to_key_input(path_cmd_char: String) -> void:
+ if inner_selections.is_empty():
# If a single path element is selected, add the new command at the end.
if selected_xids.size() == 1:
var xnode_ref := root_element.get_xnode(selected_xids[0])
if xnode_ref is ElementPath:
var path_attrib: AttributePathdata = xnode_ref.get_attribute("d")
- for action_name in path_actions_dict.keys():
- if ShortcutUtils.is_action_pressed(event, action_name):
- var path_cmd_count := path_attrib.get_command_count()
- var path_cmd_char := path_actions_dict[action_name]
- # Z after a Z is syntactically invalid.
- if (path_cmd_count == 0 and not path_cmd_char in "Mm") or\
- (path_cmd_char in "Zz" and path_cmd_count > 0 and\
- path_attrib.get_command(path_cmd_count - 1) is\
- PathCommand.CloseCommand):
- return
- path_attrib.insert_command(path_cmd_count, path_cmd_char, Vector2.ZERO)
- normal_select(selected_xids[0], path_cmd_count)
- handle_added.emit()
- break
- return
- # If path commands are selected, insert after the last one.
- for action_name in path_actions_dict.keys():
- var element_ref := root_element.get_xnode(semi_selected_xid)
- if element_ref.name == "path":
- if ShortcutUtils.is_action_pressed(event, action_name):
- var path_attrib: AttributePathdata = element_ref.get_attribute("d")
- var path_cmd_char := path_actions_dict[action_name]
- var last_selection: int = inner_selections.max()
+ var path_cmd_count := path_attrib.get_command_count()
# Z after a Z is syntactically invalid.
- if path_cmd_char in "Zz" and (path_attrib.get_command(last_selection) is\
- PathCommand.CloseCommand or (path_attrib.get_command_count() >\
- last_selection + 1 and path_attrib.get_command(last_selection + 1) is\
- PathCommand.CloseCommand)):
+ if (path_cmd_count == 0 and not path_cmd_char in "Mm") or\
+ (path_cmd_char in "Zz" and path_cmd_count > 0 and\
+ path_attrib.get_command(path_cmd_count - 1) is PathCommand.CloseCommand):
return
- path_attrib.insert_command(last_selection + 1, path_cmd_char, Vector2.ZERO)
- normal_select(semi_selected_xid, last_selection + 1)
+ path_attrib.insert_command(path_cmd_count, path_cmd_char, Vector2.ZERO)
+ normal_select(selected_xids[0], path_cmd_count)
handle_added.emit()
- break
+ else:
+ # If path commands are selected, insert after the last one.
+ var xnode_ref := root_element.get_xnode(semi_selected_xid)
+ if xnode_ref is ElementPath:
+ var path_attrib: AttributePathdata = xnode_ref.get_attribute("d")
+ var last_selection: int = inner_selections.max()
+ # Z after a Z is syntactically invalid.
+ if path_cmd_char in "Zz" and (path_attrib.get_command(last_selection) is\
+ PathCommand.CloseCommand or (path_attrib.get_command_count() >\
+ last_selection + 1 and path_attrib.get_command(last_selection + 1) is\
+ PathCommand.CloseCommand)):
+ return
+ path_attrib.insert_command(last_selection + 1, path_cmd_char, Vector2.ZERO)
+ normal_select(semi_selected_xid, last_selection + 1)
+ handle_added.emit()
# Operations on selected elements.
@@ -761,10 +765,8 @@ func get_selection_context(popup_method: Callable, context: Context) -> ContextP
btn_arr.append(ContextPopup.create_button(Translator.translate("View in List"),
view_in_list.bind(selected_xids[0]), false,
load("res://assets/icons/ViewInList.svg")))
-
- btn_arr.append(ContextPopup.create_button(Translator.translate("Duplicate"),
- duplicate_selected, false, load("res://assets/icons/Duplicate.svg"),
- "duplicate"))
+
+ btn_arr.append(ContextPopup.create_shortcut_button("duplicate"))
var xnode := root_element.get_xnode(selected_xids[0])
if selected_xids.size() == 1 and ((not xnode.is_element() and\
@@ -776,18 +778,11 @@ func get_selection_context(popup_method: Callable, context: Context) -> ContextP
load("res://assets/icons/Reload.svg")))
if can_move_up:
- btn_arr.append(ContextPopup.create_button(
- Translator.translate("Move Up"),
- move_up_selected, false,
- load("res://assets/icons/MoveUp.svg"), "move_up"))
+ btn_arr.append(ContextPopup.create_shortcut_button("move_up"))
if can_move_down:
- btn_arr.append(ContextPopup.create_button(
- Translator.translate("Move Down"),
- move_down_selected, false,
- load("res://assets/icons/MoveDown.svg"), "move_down"))
+ btn_arr.append(ContextPopup.create_shortcut_button("move_down"))
- btn_arr.append(ContextPopup.create_button(Translator.translate("Delete"),
- delete_selected, false, load("res://assets/icons/Delete.svg"), "delete"))
+ btn_arr.append(ContextPopup.create_shortcut_button("delete"))
elif not inner_selections.is_empty() and not semi_selected_xid.is_empty():
var element_ref := root_element.get_xnode(semi_selected_xid)
@@ -818,24 +813,18 @@ func get_selection_context(popup_method: Callable, context: Context) -> ContextP
var can_move_up := false
var can_move_down := false
if can_move_up:
- btn_arr.append(ContextPopup.create_button(
- Translator.translate("Move Up"), # Change to "Move Subpath Up"
- move_up_selected, false,
- load("res://visual/icons/MoveUp.svg"), "move_up"))
+ btn_arr.append(ContextPopup.create_shortcut_button("move_up"))
+ # , "Move Subpath Up"
if can_move_down:
- btn_arr.append(ContextPopup.create_button(
- Translator.translate("Move Down"), # Change to "Move Subpath Down"
- move_down_selected, false,
- load("res://visual/icons/MoveDown.svg"), "move_down"))
+ btn_arr.append(ContextPopup.create_shortcut_button("move_down"))
+ # , "Move Subpath Down"
"polygon", "polyline":
if inner_selections.size() == 1:
btn_arr.append(ContextPopup.create_button(
- Translator.translate("Insert After"),
- insert_point_after_selection, false,
- load("res://assets/icons/Plus.svg")))
+ Translator.translate("Insert After"), insert_point_after_selection,
+ false, load("res://assets/icons/Plus.svg")))
- btn_arr.append(ContextPopup.create_button(Translator.translate("Delete"),
- delete_selected, false, load("res://assets/icons/Delete.svg"), "delete"))
+ btn_arr.append(ContextPopup.create_shortcut_button("delete"))
var element_context := ContextPopup.new()
element_context.setup(btn_arr, true)
diff --git a/src/config_classes/SaveData.gd b/src/config_classes/SaveData.gd
index 9f93421..30452c9 100644
--- a/src/config_classes/SaveData.gd
+++ b/src/config_classes/SaveData.gd
@@ -23,6 +23,7 @@ func get_setting_default(setting: String) -> Variant:
"handle_selected_color": return Color("46f")
"handle_hovered_selected_color": return Color("f44")
"background_color": return Color(0.12, 0.132, 0.2, 1)
+ "grid_color": return Color(0.5, 0.5, 0.5)
"basic_color_valid": return Color("9f9")
"basic_color_error": return Color("f99")
"basic_color_warning": return Color("ee5")
@@ -172,6 +173,13 @@ const CURRENT_VERSION = 1
emit_changed()
Configs.change_background_color.call_deferred()
+@export var grid_color := Color(0.5, 0.5, 0.5):
+ set(new_value):
+ if grid_color != new_value:
+ grid_color = new_value
+ emit_changed()
+ Configs.grid_color_changed.emit()
+
@export var basic_color_valid := Color("9f9"):
set(new_value):
if basic_color_valid != new_value:
@@ -241,7 +249,7 @@ const HANDLE_SIZE_MAX = 4.0
Configs.handle_visuals_changed.emit()
enum ScalingApproach {AUTO, CONSTANT_075, CONSTANT_100, CONSTANT_125, CONSTANT_150,
- CONSTANT_175, CONSTANT_200, CONSTANT_300, CONSTANT_400, MAX}
+ CONSTANT_175, CONSTANT_200, CONSTANT_250, CONSTANT_300, CONSTANT_400, MAX}
@export var ui_scale := ScalingApproach.AUTO:
set(new_value):
# Validation
@@ -378,7 +386,7 @@ func _action_sync_inputmap(action: String) -> void:
func update_shortcut_validities() -> void:
_shortcut_validities.clear()
- for action in ShortcutUtils.get_all_shortcuts():
+ for action in ShortcutUtils.get_all_actions():
for shortcut: InputEventKey in InputMap.action_get_events(action):
var shortcut_id := shortcut.get_keycode_with_modifiers()
# If the key already exists, set validity to false, otherwise set to true.
@@ -398,7 +406,7 @@ func get_actions_with_shortcut(shortcut: InputEventKey) -> PackedStringArray:
return PackedStringArray()
var actions_with_shortcut := PackedStringArray()
- for action in ShortcutUtils.get_all_shortcuts():
+ for action in ShortcutUtils.get_all_actions():
for action_shortcut: InputEventKey in InputMap.action_get_events(action):
if action_shortcut.get_keycode_with_modifiers() == shortcut_id:
actions_with_shortcut.append(action)
@@ -520,7 +528,7 @@ const SHORTCUT_PANEL_MAX_SLOTS = 6
# Validation
for key in new_value:
if key < 0 or key >= SHORTCUT_PANEL_MAX_SLOTS or\
- not new_value[key] in ShortcutUtils.get_all_shortcuts():
+ not new_value[key] in ShortcutUtils.get_all_actions():
new_value.erase(key)
# Main part
if _shortcut_panel_slots != new_value:
@@ -576,6 +584,7 @@ const MAX_TABS = 50
for tab in _tabs:
tab.changed.connect(emit_changed)
tab.status_changed.connect(_on_tab_status_changed.bind(tab.id))
+ tab.reference_changed.connect(_on_tab_reference_changed.bind(tab.id))
emit_changed()
if _tabs.is_empty():
_add_new_tab()
@@ -599,6 +608,10 @@ func _on_tab_status_changed(id: int) -> void:
Configs.active_tab_status_changed.emit()
Configs.tabs_changed.emit()
+func _on_tab_reference_changed(id: int) -> void:
+ if id == _tabs[_active_tab_index].id:
+ Configs.active_tab_reference_changed.emit()
+
func has_tabs() -> bool:
return not _tabs.is_empty()
@@ -650,6 +663,7 @@ func _add_new_tab() -> void:
new_tab.fully_loaded = false
new_tab.changed.connect(emit_changed)
new_tab.status_changed.connect(_on_tab_status_changed.bind(new_id))
+ new_tab.reference_changed.connect(_on_tab_reference_changed.bind(new_id))
# Clear file path for the new tab.
var new_tab_path := new_tab.get_edited_file_path()
diff --git a/src/config_classes/TabData.gd b/src/config_classes/TabData.gd
index eb280ac..77c8577 100644
--- a/src/config_classes/TabData.gd
+++ b/src/config_classes/TabData.gd
@@ -7,6 +7,8 @@ const DEFAULT_SVG = '