From ceebd4a404f17ad382893be170e4f7e763144dca Mon Sep 17 00:00:00 2001 From: Anish Mishra Date: Wed, 4 Jun 2025 14:05:45 +0530 Subject: [PATCH] Update from upstream Update de.po (321) Implement use element with handles (1324) Fix elliptical arc rare visual issue (1327) Fix glitchy handle movement on android (1329) Fix tab staying empty on first open (1330) No max scale 5% rounding (1331) Fix edge case in rect drawing (1333) Add Spanish translation (1332) Fix comma parsing in transform lists (1334) Hide GUI aspects for invalid elements (1335) Add icon for the use element (1336) Implement importing multiple files at once (1337) Fix dimensionless SVG editing (1338) Some codebase comments (1339) Updated Estonian translations (1340) Speed up tab bar scrolling and use factor (1341) Update Ukrainian and Russian translations (1344) Fix SVG reference image regression (1345) Update translations for portuguese brazillian (1346) Fix tab corruption bug (1347) --- assets/icons/Snap.svg | 2 +- assets/icons/backgrounds/CheckerboardMini.svg | 2 +- assets/icons/element/circle.svg | 2 +- assets/icons/element/ellipse.svg | 2 +- assets/icons/element/g.svg | 2 +- assets/icons/element/line.svg | 2 +- assets/icons/element/linearGradient.svg | 2 +- assets/icons/element/path.svg | 2 +- assets/icons/element/polygon.svg | 2 +- assets/icons/element/stop.svg | 2 +- assets/icons/element/svg.svg | 2 +- assets/icons/element/unrecognized.svg | 2 +- assets/icons/element/use.svg | 1 + assets/icons/element/use.svg.import | 37 + assets/icons/foreign_logos/GithubLogo.svg | 2 +- assets/icons/foreign_logos/KoFiLogo.svg | 2 +- assets/icons/theme/GuiToggleChecked.svg | 2 +- godot_only/scripts/tests.gd | 38 +- project.godot | 10 +- src/autoload/Configs.gd | 14 +- src/autoload/HandlerGUI.gd | 23 +- src/autoload/State.gd | 13 +- src/config_classes/SaveData.gd | 14 +- src/config_classes/TabData.gd | 23 +- src/data_classes/Attribute.gd | 23 + src/data_classes/AttributeHref.gd | 13 + src/data_classes/AttributeHref.gd.uid | 1 + src/data_classes/AttributeID.gd | 28 +- src/data_classes/AttributeTransformList.gd | 6 +- src/data_classes/ColorParser.gd | 2 +- src/data_classes/DB.gd | 15 +- src/data_classes/Element.gd | 8 + src/data_classes/ElementRoot.gd | 2 +- src/data_classes/ElementSVG.gd | 4 +- src/data_classes/ElementUse.gd | 18 + src/data_classes/ElementUse.gd.uid | 1 + src/ui_parts/good_file_dialog.gd | 220 +-- src/ui_parts/good_file_dialog.tscn | 2 - src/ui_parts/handles_manager.gd | 10 +- src/ui_parts/tab_bar.gd | 97 +- src/ui_widgets/alert_dialog.tscn | 3 +- src/ui_widgets/color_field_popup.gd | 2 +- src/ui_widgets/confirm_dialog.tscn | 3 +- src/ui_widgets/element_frame.gd | 1 + src/ui_widgets/href_field.gd | 46 + src/ui_widgets/href_field.gd.uid | 1 + src/ui_widgets/href_field.tscn | 12 + src/ui_widgets/id_field.gd | 6 +- src/ui_widgets/options_dialog.gd | 58 +- src/ui_widgets/options_dialog.tscn | 27 +- src/utils/AttributeFieldBuilder.gd | 2 + src/utils/FileUtils.gd | 192 ++- src/utils/ShortcutUtils.gd | 10 +- src/utils/TranslationUtils.gd | 33 +- src/utils/Utils.gd | 13 + translations/GodSVG.pot | 68 +- translations/bg.po | 76 +- translations/de.po | 94 +- translations/en.po | 68 +- translations/es.po | 1340 +++++++++++++++++ translations/et.po | 105 +- translations/fr.po | 83 +- translations/nl.po | 83 +- translations/pt_BR.po | 159 +- translations/ru.po | 94 +- translations/uk.po | 93 +- translations/zh_CN.po | 80 +- 67 files changed, 2772 insertions(+), 633 deletions(-) create mode 100644 assets/icons/element/use.svg create mode 100644 assets/icons/element/use.svg.import create mode 100644 src/data_classes/AttributeHref.gd create mode 100644 src/data_classes/AttributeHref.gd.uid create mode 100644 src/data_classes/ElementUse.gd create mode 100644 src/data_classes/ElementUse.gd.uid create mode 100644 src/ui_widgets/href_field.gd create mode 100644 src/ui_widgets/href_field.gd.uid create mode 100644 src/ui_widgets/href_field.tscn create mode 100644 translations/es.po diff --git a/assets/icons/Snap.svg b/assets/icons/Snap.svg index 8edc101..4f39fe8 100644 --- a/assets/icons/Snap.svg +++ b/assets/icons/Snap.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/backgrounds/CheckerboardMini.svg b/assets/icons/backgrounds/CheckerboardMini.svg index 0f1f3d1..d185f8e 100644 --- a/assets/icons/backgrounds/CheckerboardMini.svg +++ b/assets/icons/backgrounds/CheckerboardMini.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/element/circle.svg b/assets/icons/element/circle.svg index f9edc29..0befcc7 100644 --- a/assets/icons/element/circle.svg +++ b/assets/icons/element/circle.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/element/ellipse.svg b/assets/icons/element/ellipse.svg index 1c4e677..17de190 100644 --- a/assets/icons/element/ellipse.svg +++ b/assets/icons/element/ellipse.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/element/g.svg b/assets/icons/element/g.svg index 9921ebb..344393c 100644 --- a/assets/icons/element/g.svg +++ b/assets/icons/element/g.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/line.svg b/assets/icons/element/line.svg index d9b5e2a..8f49bee 100644 --- a/assets/icons/element/line.svg +++ b/assets/icons/element/line.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/element/linearGradient.svg b/assets/icons/element/linearGradient.svg index 10491f3..058681e 100644 --- a/assets/icons/element/linearGradient.svg +++ b/assets/icons/element/linearGradient.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/path.svg b/assets/icons/element/path.svg index 35ade79..bf27000 100644 --- a/assets/icons/element/path.svg +++ b/assets/icons/element/path.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/polygon.svg b/assets/icons/element/polygon.svg index 8ef7302..334e157 100644 --- a/assets/icons/element/polygon.svg +++ b/assets/icons/element/polygon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/stop.svg b/assets/icons/element/stop.svg index ac42d55..c490893 100644 --- a/assets/icons/element/stop.svg +++ b/assets/icons/element/stop.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/svg.svg b/assets/icons/element/svg.svg index 70eeeb9..ad5711a 100644 --- a/assets/icons/element/svg.svg +++ b/assets/icons/element/svg.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/element/unrecognized.svg b/assets/icons/element/unrecognized.svg index 97b8fb4..d020e43 100644 --- a/assets/icons/element/unrecognized.svg +++ b/assets/icons/element/unrecognized.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/assets/icons/element/use.svg b/assets/icons/element/use.svg new file mode 100644 index 0000000..4301e10 --- /dev/null +++ b/assets/icons/element/use.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/element/use.svg.import b/assets/icons/element/use.svg.import new file mode 100644 index 0000000..cc0ca0a --- /dev/null +++ b/assets/icons/element/use.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c1k88bqfhsvp5" +path="res://.godot/imported/use.svg-7ae354a656abf9c2d947f0f7d986cf9a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icons/element/use.svg" +dest_files=["res://.godot/imported/use.svg-7ae354a656abf9c2d947f0f7d986cf9a.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/foreign_logos/GithubLogo.svg b/assets/icons/foreign_logos/GithubLogo.svg index 5d796c8..91ff8ff 100644 --- a/assets/icons/foreign_logos/GithubLogo.svg +++ b/assets/icons/foreign_logos/GithubLogo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/foreign_logos/KoFiLogo.svg b/assets/icons/foreign_logos/KoFiLogo.svg index 4d26af8..30b1d02 100644 --- a/assets/icons/foreign_logos/KoFiLogo.svg +++ b/assets/icons/foreign_logos/KoFiLogo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/assets/icons/theme/GuiToggleChecked.svg b/assets/icons/theme/GuiToggleChecked.svg index 3a96e91..c3ed5f0 100644 --- a/assets/icons/theme/GuiToggleChecked.svg +++ b/assets/icons/theme/GuiToggleChecked.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/godot_only/scripts/tests.gd b/godot_only/scripts/tests.gd index 46e1148..38a96a2 100644 --- a/godot_only/scripts/tests.gd +++ b/godot_only/scripts/tests.gd @@ -5,12 +5,11 @@ extends EditorScript func _run() -> void: - var pathdata_test_passed := pathdata_tests() - if pathdata_test_passed: - print("All tests passed!") + pathdata_tests() + transform_list_tests() -func pathdata_tests(print_success := false) -> bool: +func pathdata_tests() -> void: const tests: Dictionary[String, Array] = { "Jerky": [], "M 3s 6 h 6 v 3 z": [], @@ -31,13 +30,34 @@ func pathdata_tests(print_success := false) -> bool: "M1 6.9e-1": [["M", 1.0, 0.69]], } - var tests_passed := true for test in tests: var result := AttributePathdata.pathdata_to_arrays(test) var expected: Array = tests[test] if result != expected: - tests_passed = false print('"' + test + '" generated ' + str(result) + ', expected ' + str(expected)) - elif print_success: - print('"' + test + '" generated ' + str(result) + ' (SUCCESS)') - return tests_passed + +func transform_list_tests() -> void: + var tests: Dictionary[String, Array] = { + "Jerky": [], + "matrix(1, 0, 0, 5, 0, 3)": [Transform.TransformMatrix.new(1, 0, 0, 5, 0, 3)], + "matrix(1 0 0 5 0 3)": [Transform.TransformMatrix.new(1, 0, 0, 5, 0, 3)], + } + + for test in tests: + var test_passed := true + var result := AttributeTransformList.text_to_transform_list(test) + var expected := tests[test] + if expected.size() != result.size(): + test_passed = false + else: + for i in expected.size(): + if expected[i] is Transform.TransformMatrix and\ + (not result[i] is Transform.TransformMatrix or\ + expected[i].x1 != result[i].x1 or expected[i].x2 != result[i].x2 or\ + expected[i].y1 != result[i].y1 or expected[i].y2 != result[i].y2 or\ + expected[i].o1 != result[i].o1 or expected[i].o2 != result[i].o2): + test_passed = false + break + + if not test_passed: + print('"' + test + '" generated ' + str(result)) diff --git a/project.godot b/project.godot index 4e74675..1669b9d 100644 --- a/project.godot +++ b/project.godot @@ -119,6 +119,14 @@ close_all_other_tabs={ "deadzone": 0.2, "events": [] } +close_empty_tabs={ +"deadzone": 0.2, +"events": [] +} +close_saved_tabs={ +"deadzone": 0.2, +"events": [] +} new_tab={ "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":84,"physical_keycode":0,"key_label":0,"unicode":116,"location":0,"echo":false,"script":null) @@ -358,7 +366,7 @@ pen_tablet/driver.windows="dummy" [internationalization] rendering/root_node_auto_translate=false -locale/translations=PackedStringArray("res://translations/bg.po", "res://translations/de.po", "res://translations/et.po", "res://translations/en.po", "res://translations/fr.po", "res://translations/nl.po", "res://translations/pt_BR.po", "res://translations/ru.po", "res://translations/uk.po", "res://translations/zh_CN.po") +locale/translations=PackedStringArray("res://translations/bg.po", "res://translations/de.po", "res://translations/es.po", "res://translations/et.po", "res://translations/en.po", "res://translations/fr.po", "res://translations/nl.po", "res://translations/pt_BR.po", "res://translations/ru.po", "res://translations/uk.po", "res://translations/zh_CN.po") [physics] diff --git a/src/autoload/Configs.gd b/src/autoload/Configs.gd index 072df73..edcfde7 100644 --- a/src/autoload/Configs.gd +++ b/src/autoload/Configs.gd @@ -66,9 +66,7 @@ func load_config() -> void: reset_settings() return - savedata.get_active_tab().activate() - change_background_color() - change_locale() + post_load() func reset_settings() -> void: @@ -78,6 +76,12 @@ func reset_settings() -> void: savedata.set_shortcut_panel_slots({ 0: "ui_undo", 1: "ui_redo", 2: "duplicate", 3: "save" }) savedata.set_palettes([Palette.new("Pure", Palette.Preset.PURE)]) save() + post_load() + +func post_load() -> void: + savedata.get_active_tab().activate() + sync_background_color() + sync_locale() func generate_highlighter() -> SVGHighlighter: @@ -95,10 +99,10 @@ func generate_highlighter() -> SVGHighlighter: # Global effects from settings. Some of them should also be used on launch. -func change_background_color() -> void: +func sync_background_color() -> void: RenderingServer.set_default_clear_color(savedata.background_color) -func change_locale() -> void: +func sync_locale() -> void: if not savedata.language in TranslationServer.get_loaded_locales(): savedata.language = "en" else: diff --git a/src/autoload/HandlerGUI.gd b/src/autoload/HandlerGUI.gd index 77c33a4..e25bc3e 100644 --- a/src/autoload/HandlerGUI.gd +++ b/src/autoload/HandlerGUI.gd @@ -43,7 +43,7 @@ func _notification(what: int) -> void: # Drag-and-drop of files. func _on_files_dropped(files: PackedStringArray) -> void: if menu_stack.is_empty(): - FileUtils.apply_svg_from_path(files[0]) + FileUtils.apply_svgs_from_paths(files) func add_menu(new_menu: Control) -> void: @@ -263,15 +263,21 @@ func _unhandled_input(event: InputEvent) -> void: "save": FileUtils.save_svg() "save_as": FileUtils.save_svg_as() "close_tab": FileUtils.close_tabs(Configs.savedata.get_active_tab_index()) + "close_all_other_tabs": FileUtils.close_tabs( + Configs.savedata.get_active_tab_index(), + FileUtils.TabCloseMode.ALL_OTHERS) "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( + "close_empty_tabs": FileUtils.close_tabs( Configs.savedata.get_active_tab_index(), - FileUtils.TabCloseMode.ALL_OTHERS) + FileUtils.TabCloseMode.EMPTY) + "close_saved_tabs": FileUtils.close_tabs( + Configs.savedata.get_active_tab_index(), + FileUtils.TabCloseMode.SAVED) "new_tab": Configs.savedata.add_empty_tab() "select_next_tab": Configs.savedata.set_active_tab_index( posmod(Configs.savedata.get_active_tab_index() + 1, @@ -359,7 +365,7 @@ func get_max_ui_scale() -> float: var window_default_size := get_window_default_size() # How much can the default size be increased before it takes all usable screen space. var max_expansion := Vector2(usable_screen_size) / Vector2(window_default_size) - return clampf(snappedf(minf(max_expansion.x, max_expansion.y) - 0.025, 0.05), 0.75, 4.0) + return clampf(snappedf(minf(max_expansion.x, max_expansion.y) - 0.005, 0.01), 0.75, 4.0) func get_min_ui_scale() -> float: return maxf(snappedf(get_max_ui_scale() / 2.0 - 0.125, 0.25), 0.75) @@ -519,7 +525,14 @@ func throw_mouse_motion_event() -> void: var mm_event := InputEventMouseMotion.new() var window := get_window() # Must multiply by the final transform because the InputEvent is not yet parsed. - mm_event.position = window.get_mouse_position() * window.get_final_transform() + var mouse_position = window.get_mouse_position() + # TODO This is a workaround because the returned mouse position is sometimes (0, 0), + # likely a Godot issue. This has been reproduced on Android and on Web. + # Reproducing on web is especially easy with zoom at something like 110% on Web. + if mouse_position == Vector2.ZERO: + return + + mm_event.position = mouse_position * window.get_final_transform() Input.parse_input_event.call_deferred(mm_event) # Trigger a shortcut automatically. diff --git a/src/autoload/State.gd b/src/autoload/State.gd index 91903c3..3e36ddd 100644 --- a/src/autoload/State.gd +++ b/src/autoload/State.gd @@ -71,16 +71,9 @@ func _enter_tree() -> void: cmdline_args.remove_at(0) if cmdline_args.size() >= 1: - # Need to wait a frame so the import warnings panel can be added. + # Need to wait a frame so the import warnings panel becomes available. 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) + FileUtils.apply_svgs_from_paths(cmdline_args) func setup_from_tab() -> void: var active_tab := Configs.savedata.get_active_tab() @@ -91,7 +84,7 @@ func setup_from_tab() -> void: return if not new_text.is_empty(): - apply_svg_text(new_text) + apply_svg_text(new_text, false) return if active_tab.fully_loaded and not active_tab.empty_unsaved and\ diff --git a/src/config_classes/SaveData.gd b/src/config_classes/SaveData.gd index 35db893..8ba9e8a 100644 --- a/src/config_classes/SaveData.gd +++ b/src/config_classes/SaveData.gd @@ -63,7 +63,7 @@ func validate() -> void: if _layout.has(location) and not _layout[location].is_empty(): return _layout = { - LayoutLocation.TOP_LEFT: [Utils.LayoutPart.CODE_EDITOR, Utils.LayoutPart.INSPECTOR] + LayoutLocation.TOP_LEFT: [Utils.LayoutPart.INSPECTOR, Utils.LayoutPart.CODE_EDITOR] } @@ -79,7 +79,7 @@ const CURRENT_VERSION = 1 if language != new_value: language = new_value emit_changed() - Configs.change_locale.call_deferred() + Configs.sync_locale.call_deferred() Configs.language_changed.emit.call_deferred() # Theming @@ -179,7 +179,7 @@ const CURRENT_VERSION = 1 if background_color != new_value: background_color = new_value emit_changed() - Configs.change_background_color.call_deferred() + Configs.sync_background_color.call_deferred() @export var grid_color := Color(0.5, 0.5, 0.5): set(new_value): @@ -571,7 +571,7 @@ func erase_shortcut_panel_slot(slot: int) -> void: Configs.shortcut_panel_changed.emit() -const MAX_TABS = 50 +const MAX_TABS = 4096 @export var _tabs: Array[TabData] = []: set(new_value): # Validation @@ -657,6 +657,7 @@ func set_active_tab_index(new_index: int) -> void: if old_id != _tabs[_active_tab_index].id: Configs.active_tab_changed.emit() +# Basic operation that all tab adding methods call. func _add_new_tab() -> void: if _tabs.size() >= MAX_TABS: return @@ -689,7 +690,8 @@ func add_empty_tab() -> void: Configs.tabs_changed.emit() set_active_tab_index(_tabs.size() - 1) -# Adds a new path with the given path, unless something with the path already exists. +# Adds a new path with the given path. +# If a tab with the path already exists, it's set as the active tab instead. func add_tab_with_path(new_file_path: String) -> void: for idx in _tabs.size(): if _tabs[idx].svg_file_path == new_file_path: @@ -701,6 +703,8 @@ func add_tab_with_path(new_file_path: String) -> void: Configs.tabs_changed.emit() set_active_tab_index(_tabs.size() - 1) +# Note that a method for removing multiple tabs at once isn't straightforward, +# since removed tabs can show dialogs asking the user if they should be saved. func remove_tab(idx: int) -> void: if idx < 0 or idx >= _tabs.size(): return diff --git a/src/config_classes/TabData.gd b/src/config_classes/TabData.gd index 77c8577..dc7879f 100644 --- a/src/config_classes/TabData.gd +++ b/src/config_classes/TabData.gd @@ -116,18 +116,9 @@ func get_edited_file_path() -> String: static func get_edited_file_path_for_id(checked_id: int) -> String: return "%s/save%d.svg" % [EDITED_FILES_DIR, checked_id] -# Method for showing the file path without stuff like "/home/mewpurpur/". -# This information is pretty much always unnecessary clutter. + func get_presented_svg_file_path() -> String: - var home_dir: String - if OS.get_name() == "Windows": - home_dir = OS.get_environment("USERPROFILE") - else: - home_dir = OS.get_environment("HOME") - - if svg_file_path.begins_with(home_dir): - return svg_file_path.trim_prefix(home_dir).trim_prefix("/").trim_prefix("\\") - return svg_file_path + return Utils.simplify_file_path(svg_file_path) func undo() -> void: @@ -142,7 +133,7 @@ func redo() -> void: func _on_language_changed() -> void: - if svg_file_path.is_empty(): + if not is_saved(): queue_sync() func queue_sync() -> void: @@ -154,7 +145,7 @@ func _sync() -> void: return _sync_pending = false - if not svg_file_path.is_empty(): + if is_saved(): # The extension is included in the presented name too. # It's always in the end anyway so it can't hide useless info. # And also, it prevents ".svg" from being presented as an empty string. @@ -194,3 +185,9 @@ func deactivate() -> void: func get_true_svg_text() -> String: return _svg_text if active else FileAccess.get_file_as_string(get_edited_file_path()) + +func is_empty() -> bool: + return empty_unsaved + +func is_saved() -> bool: + return not svg_file_path.is_empty() diff --git a/src/data_classes/Attribute.gd b/src/data_classes/Attribute.gd index f6b3cf6..c0d9c80 100644 --- a/src/data_classes/Attribute.gd +++ b/src/data_classes/Attribute.gd @@ -4,6 +4,8 @@ class_name Attribute extends RefCounted signal value_changed +enum NameValidityLevel {VALID, INVALID_XML_NAMETOKEN, INVALID} + var name: String var _value: String @@ -35,3 +37,24 @@ func _format(text: String, _formatter: Formatter) -> String: func _init(new_name: String, init_value := "") -> void: name = new_name set_value(init_value) + +static func get_name_validity(id: String) -> NameValidityLevel: + var validity_level := NameValidityLevel.VALID + for id_char in id: + if id_char in ":_-.": + continue + var u := id_char.unicode_at(0) + if (u >= 48 and u <= 57) or (u >= 65 and u <= 90) or (u >= 97 and u <= 122) or\ + (u >= 0xC0 and u <= 0xD6) or (u >= 0xD8 and u <= 0xF6) or\ + (u >= 0xF8 and u <= 0x2FF) or (u >= 0x370 and u <= 0x37D) or\ + (u >= 0x37F and u <= 0x1FFF) or (u >= 0x200C and u <= 0x200D) or\ + (u >= 0x2070 and u <= 0x218F) or (u >= 0x2C00 and u <= 0x2FEF) or\ + (u >= 0x3001 and u <= 0xD7FF) or (u >= 0xF900 and u <= 0xFDCF) or\ + (u >= 0xFDF0 and u <= 0xFFFD) or (u >= 0x10000 and u <= 0xEFFFF): + continue + + if id_char in " \n\r\t\r": + return NameValidityLevel.INVALID + else: + validity_level = NameValidityLevel.INVALID_XML_NAMETOKEN + return validity_level diff --git a/src/data_classes/AttributeHref.gd b/src/data_classes/AttributeHref.gd new file mode 100644 index 0000000..cdcb7c7 --- /dev/null +++ b/src/data_classes/AttributeHref.gd @@ -0,0 +1,13 @@ +# An attribute representing an element's href +class_name AttributeHref extends Attribute + +func set_value(new_value: String) -> void: + super(new_value if get_validity(new_value) != NameValidityLevel.INVALID else "") + + +static func get_validity(id: String) -> NameValidityLevel: + if id.is_empty(): + return NameValidityLevel.INVALID + + # Allow '#'. + return get_name_validity(id.trim_prefix("#")) diff --git a/src/data_classes/AttributeHref.gd.uid b/src/data_classes/AttributeHref.gd.uid new file mode 100644 index 0000000..ad19a62 --- /dev/null +++ b/src/data_classes/AttributeHref.gd.uid @@ -0,0 +1 @@ +uid://dwywmm3ljqfrg diff --git a/src/data_classes/AttributeID.gd b/src/data_classes/AttributeID.gd index 5b624dc..e769b3c 100644 --- a/src/data_classes/AttributeID.gd +++ b/src/data_classes/AttributeID.gd @@ -1,32 +1,12 @@ # An attribute representing an element's id class_name AttributeID extends Attribute -enum ValidityLevel {VALID, INVALID_XML_NAMETOKEN, INVALID} - func set_value(new_value: String) -> void: - super(new_value if get_validity(new_value) != ValidityLevel.INVALID else "") + super(new_value if get_validity(new_value) != NameValidityLevel.INVALID else "") -static func get_validity(id: String) -> ValidityLevel: +static func get_validity(id: String) -> NameValidityLevel: if id.is_empty() or id[0] == "#": - return ValidityLevel.INVALID + return NameValidityLevel.INVALID - var validity_level := ValidityLevel.VALID - for id_char in id: - if id_char in ":_-.": - continue - var u := id_char.unicode_at(0) - if (u >= 48 and u <= 57) or (u >= 65 and u <= 90) or (u >= 97 and u <= 122) or\ - (u >= 0xC0 and u <= 0xD6) or (u >= 0xD8 and u <= 0xF6) or\ - (u >= 0xF8 and u <= 0x2FF) or (u >= 0x370 and u <= 0x37D) or\ - (u >= 0x37F and u <= 0x1FFF) or (u >= 0x200C and u <= 0x200D) or\ - (u >= 0x2070 and u <= 0x218F) or (u >= 0x2C00 and u <= 0x2FEF) or\ - (u >= 0x3001 and u <= 0xD7FF) or (u >= 0xF900 and u <= 0xFDCF) or\ - (u >= 0xFDF0 and u <= 0xFFFD) or (u >= 0x10000 and u <= 0xEFFFF): - continue - - if id_char in " \n\r\t\r": - return ValidityLevel.INVALID - else: - validity_level = ValidityLevel.INVALID_XML_NAMETOKEN - return validity_level + return get_name_validity(id) diff --git a/src/data_classes/AttributeTransformList.gd b/src/data_classes/AttributeTransformList.gd index d71908f..6bedb0c 100644 --- a/src/data_classes/AttributeTransformList.gd +++ b/src/data_classes/AttributeTransformList.gd @@ -123,15 +123,15 @@ static func text_to_transform_list(text: String) -> Array[Transform]: @warning_ignore("shadowed_global_identifier") var char := transform_params[idx] - if comma_exhausted and not char in " \n\t\r": - comma_exhausted = false - var start_idx := idx var end_idx := idx var number_proceed := true var passed_decimal_point := false var exponent_just_passed := true while number_proceed and idx < transform_params.length(): + if comma_exhausted and not char in " \n\t\r": + comma_exhausted = false + char = transform_params[idx] match char: "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": diff --git a/src/data_classes/ColorParser.gd b/src/data_classes/ColorParser.gd index f3107e2..cc2cc5f 100644 --- a/src/data_classes/ColorParser.gd +++ b/src/data_classes/ColorParser.gd @@ -95,7 +95,7 @@ static func is_valid_url(color: String) -> bool: color = color.strip_edges() if not color.begins_with("url(") or not color.ends_with(")"): return false - return AttributeID.get_validity(_get_url_id(color)) != AttributeID.ValidityLevel.INVALID + return AttributeID.get_validity(_get_url_id(color)) != Attribute.NameValidityLevel.INVALID static func _get_url_id(stripped_color: String) -> String: return stripped_color.substr(4, diff --git a/src/data_classes/DB.gd b/src/data_classes/DB.gd index c8bcdf2..08e1ca2 100644 --- a/src/data_classes/DB.gd +++ b/src/data_classes/DB.gd @@ -1,12 +1,13 @@ class_name DB extends RefCounted -enum AttributeType {NUMERIC, COLOR, LIST, PATHDATA, ENUM, TRANSFORM_LIST, ID, UNKNOWN} +enum AttributeType {NUMERIC, COLOR, LIST, PATHDATA, ENUM, TRANSFORM_LIST, ID, HREF, UNKNOWN} enum PercentageHandling {FRACTION, HORIZONTAL, VERTICAL, NORMALIZED} enum NumberRange {ARBITRARY, POSITIVE, UNIT} const recognized_elements: Array[String] = ["svg", "g", "circle", "ellipse", "rect", - "path", "line", "polyline", "polygon", "stop", "linearGradient", "radialGradient"] + "path", "line", "polyline", "polygon", "stop", "linearGradient", "radialGradient", + "use"] const _element_icons: Dictionary[String, Texture2D] = { "circle": preload("res://assets/icons/element/circle.svg"), @@ -21,6 +22,7 @@ const _element_icons: Dictionary[String, Texture2D] = { "linearGradient": preload("res://assets/icons/element/linearGradient.svg"), "radialGradient": preload("res://assets/icons/element/radialGradient.svg"), "stop": preload("res://assets/icons/element/stop.svg"), + "use": preload("res://assets/icons/element/use.svg"), } const _unrecognized_xnode_icon = preload("res://assets/icons/element/unrecognized.svg") @@ -57,13 +59,14 @@ const recognized_attributes: Dictionary[String, Array] = { "polyline": ["transform", "opacity", "fill", "fill-opacity", "stroke", "stroke-opacity", "stroke-width", "stroke-linecap", "stroke-linejoin", "points"], "stop": ["offset", "stop-color", "stop-opacity"], + "use": ["href", "transform", "x", "y"] } const _valid_children: Dictionary[String, Array] = { "svg": ["svg", "path", "circle", "ellipse", "rect", "line", "polygon", "polyline", - "g", "linearGradient", "radialGradient"], + "g", "linearGradient", "radialGradient", "use"], "g": ["svg", "path", "circle", "ellipse", "rect", "line", "polygon", "polyline", - "g", "linearGradient", "radialGradient"], + "g", "linearGradient", "radialGradient", "use"], "linearGradient": ["stop"], "radialGradient": ["stop"], "circle": [], @@ -74,6 +77,7 @@ const _valid_children: Dictionary[String, Array] = { "polygon": [], "polyline": [], "stop": [], + "use": [], } const propagated_attributes: Array[String] = ["fill", "fill-opacity", "stroke", @@ -113,6 +117,7 @@ const _attribute_types: Dictionary[String, AttributeType] = { "gradientTransform": AttributeType.TRANSFORM_LIST, "gradientUnits": AttributeType.ENUM, "spreadMethod": AttributeType.ENUM, + "href": AttributeType.HREF, } const attribute_enum_values: Dictionary[String, Array] = { @@ -218,6 +223,7 @@ static func element(name: String) -> Element: "linearGradient": return ElementLinearGradient.new() "radialGradient": return ElementRadialGradient.new() "stop": return ElementStop.new() + "use": return ElementUse.new() _: return ElementUnrecognized.new(name) static func attribute(name: String, value: String) -> Attribute: @@ -229,6 +235,7 @@ static func attribute(name: String, value: String) -> Attribute: DB.AttributeType.ENUM: return AttributeEnum.new(name, value) DB.AttributeType.TRANSFORM_LIST: return AttributeTransformList.new(name, value) DB.AttributeType.ID: return AttributeID.new(name, value) + DB.AttributeType.HREF: return AttributeHref.new(name, value) _: return Attribute.new(name, value) diff --git a/src/data_classes/Element.gd b/src/data_classes/Element.gd index 9525162..554fb7f 100644 --- a/src/data_classes/Element.gd +++ b/src/data_classes/Element.gd @@ -60,6 +60,14 @@ func get_all_element_descendants() -> Array[Element]: elements += child.get_all_element_descendants() return elements +func get_all_valid_element_descendants() -> Array[Element]: + var elements: Array[Element] = [] + for child in get_children(): + if child.is_element() and DB.is_child_element_valid(self.name, child.name): + elements.append(child) + elements += child.get_all_valid_element_descendants() + return elements + # Gets the basic XML nodes too. func get_all_xnode_descendants() -> Array[XNode]: var xnodes: Array[XNode] = [] diff --git a/src/data_classes/ElementRoot.gd b/src/data_classes/ElementRoot.gd index 61e9504..cd5734f 100644 --- a/src/data_classes/ElementRoot.gd +++ b/src/data_classes/ElementRoot.gd @@ -28,7 +28,7 @@ func get_xnode(loc: PackedInt32Array) -> XNode: return current_element func get_element_by_id(id: String) -> Element: - for element in get_all_element_descendants(): + for element in get_all_valid_element_descendants(): if element.get_attribute_value("id") == id: return element return null diff --git a/src/data_classes/ElementSVG.gd b/src/data_classes/ElementSVG.gd index 463aef4..25dde34 100644 --- a/src/data_classes/ElementSVG.gd +++ b/src/data_classes/ElementSVG.gd @@ -1,9 +1,6 @@ # An element. class_name ElementSVG extends Element -# TODO Fix up the logic for handling x, y, and aspect ratio handling. -#var x: float -#var y: float var width: float var height: float var normalized_diagonal: float @@ -19,6 +16,7 @@ const name = "svg" const possible_conversions: Array[String] = [] func _init() -> void: + canvas_precise_transform = PackedFloat64Array([1.0, 0.0, 0.0, 1.0, 0.0, 0.0]) attribute_changed.connect(_conditional_update_cache) # If attributes change in an ancestor, it can affect percentage calculations. ancestor_attribute_changed.connect(_conditional_update_cache) diff --git a/src/data_classes/ElementUse.gd b/src/data_classes/ElementUse.gd new file mode 100644 index 0000000..f8127e4 --- /dev/null +++ b/src/data_classes/ElementUse.gd @@ -0,0 +1,18 @@ +# A element. +class_name ElementUse extends Element + +const name = "use" +const possible_conversions: Array[String] = [] + +func user_setup(precise_pos := PackedFloat64Array([0.0, 0.0])) -> void: + if precise_pos != PackedFloat64Array([0.0, 0.0]): + set_attribute("x", precise_pos[0]) + set_attribute("y", precise_pos[1]) + +func get_bounding_box() -> Rect2: + return Rect2() + +func _get_own_default(attribute_name: String) -> String: + match attribute_name: + "x", "y": return "0" + _: return "" diff --git a/src/data_classes/ElementUse.gd.uid b/src/data_classes/ElementUse.gd.uid new file mode 100644 index 0000000..468bc5e --- /dev/null +++ b/src/data_classes/ElementUse.gd.uid @@ -0,0 +1 @@ +uid://j6pjh0nn1f47 diff --git a/src/ui_parts/good_file_dialog.gd b/src/ui_parts/good_file_dialog.gd index 409076d..8272a7a 100644 --- a/src/ui_parts/good_file_dialog.gd +++ b/src/ui_parts/good_file_dialog.gd @@ -5,7 +5,8 @@ const ChooseNameDialogScene = preload("res://src/ui_widgets/choose_name_dialog.t const ConfirmDialogScene = preload("res://src/ui_widgets/confirm_dialog.tscn") const AlertDialogScene = preload("res://src/ui_widgets/alert_dialog.tscn") -signal file_selected(path: String) +# Full absolute file paths of all selected files. +signal files_selected(paths: PackedStringArray) const folder_icon = preload("res://assets/icons/Folder.svg") const broken_file_icon = preload("res://assets/icons/FileBroken.svg") @@ -15,17 +16,16 @@ const system_dirs_to_show: Array[OS.SystemDir] = [OS.SYSTEM_DIR_DESKTOP, OS.SYSTEM_DIR_DOCUMENTS, OS.SYSTEM_DIR_DOWNLOADS, OS.SYSTEM_DIR_MOVIES, OS.SYSTEM_DIR_MUSIC, OS.SYSTEM_DIR_PICTURES] -enum FileMode {SELECT, SAVE} +enum FileMode {SELECT, MULTI_SELECT, SAVE} var mode: FileMode var current_dir := "" -var current_file := "" -var default_file := "" # The file you opened this dialog with. var extensions := PackedStringArray() - var item_height := 16 var search_text := "" +var default_saved_file := "" # The file you opened this dialog with. + var dir_cursor: DirAccess @onready var title_label: Label = $VBoxContainer/TitleLabel @@ -61,9 +61,20 @@ class Actions: selection_callback = on_selection right_click_callback = on_right_click + +# Queueing is necessary for this one because in Godot, "Enter" is hard-coded +# to activate the selected items. +var _activation_callback_pending := false func call_activation_callback(actions: Actions) -> void: - if is_instance_valid(actions) and actions.activation_callback.is_valid(): + var actual_activation_callback := func() -> void: + if not _activation_callback_pending: + return + _activation_callback_pending = false actions.activation_callback.call() + + if is_instance_valid(actions) and actions.activation_callback.is_valid(): + actual_activation_callback.call_deferred() + _activation_callback_pending = true func call_selection_callback(actions: Actions) -> void: if is_instance_valid(actions) and actions.selection_callback.is_valid(): @@ -77,23 +88,27 @@ func call_right_click_callback(actions: Actions) -> void: func setup(new_dir: String, new_file: String, new_mode: FileMode, new_extensions: PackedStringArray) -> void: current_dir = new_dir - current_file = new_file if new_mode == FileMode.SAVE: - default_file = new_file + default_saved_file = new_file mode = new_mode extensions = new_extensions func _ready() -> void: # Signal connections. + folder_up_button.pressed.connect(_on_folder_up_button_pressed) + file_list.item_selected.connect(_on_file_list_item_selected) + file_list.multi_selected.connect(_on_file_list_item_multi_selected) refresh_button.pressed.connect(refresh_dir) close_button.pressed.connect(queue_free) - file_selected.connect(queue_free.unbind(1)) - special_button.pressed.connect(select_file) + files_selected.connect(queue_free.unbind(1)) + special_button.pressed.connect(select_files) file_list.get_v_scroll_bar().value_changed.connect(_setup_file_images.unbind(1)) # Rest of setup. - if mode == FileMode.SELECT: + if mode != FileMode.SAVE: file_container.hide() + if mode == FileMode.MULTI_SELECT: + file_list.select_mode = ItemList.SELECT_MULTI var extension_panel_stylebox := extension_panel.get_theme_stylebox("panel") extension_panel_stylebox.content_margin_top -= 4.0 @@ -111,46 +126,45 @@ func _ready() -> void: title_label.text = TranslationUtils.get_file_dialog_save_mode_title_text(extensions[0]) extension_label.text = "." + extensions[0] else: - title_label.text = TranslationUtils.get_file_dialog_select_mode_title_text(extensions) + title_label.text = TranslationUtils.get_file_dialog_select_mode_title_text( + mode == FileMode.MULTI_SELECT, extensions) close_button.text = Translator.translate("Close") - special_button.text = Translator.translate("Select") if\ - mode == FileMode.SELECT else Translator.translate("Save") + special_button.text = Translator.translate("Save") if\ + mode == FileMode.SAVE else Translator.translate("Select") path_label.text = Translator.translate("Path") + ":" # Should always be safe. refresh_dir() if mode == FileMode.SAVE: - set_file(current_file) + sync_file_field() file_field.grab_focus() else: special_button.disabled = true special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW -func enter_dir(dir: String) -> void: - if search_button.button_pressed: - search_button.button_pressed = false - set_dir(dir) - -func refresh_dir() -> void: - set_dir(current_dir) - func file_sort(file1: String, file2: String) -> bool: return file1.naturalnocasecmp_to(file2) == -1 -# This function requires a safe input. -func set_dir(dir: String) -> void: +func refresh_dir() -> void: + open_dir(current_dir) + +func open_dir(dir: String) -> void: + if dir != current_dir and search_button.button_pressed: + search_button.button_pressed = false + dir_cursor = DirAccess.open(dir) - if !is_instance_valid(dir_cursor): + if not is_instance_valid(dir_cursor): + # TODO implement a fallback. return file_list.clear() file_list.get_v_scroll_bar().value = 0 # Basic setup. - unfocus_file() current_dir = dir - path_field.text = current_dir + sync_to_selection() + sync_path_field() dir_cursor.include_hidden = Configs.savedata.file_dialog_show_hidden # Rebuild the system dirs, as we may now need to highlight the current one. drives_list.clear() @@ -162,7 +176,7 @@ func set_dir(dir: String) -> void: var item_idx := drives_list.add_item(drive_name, get_drive_icon(drive_path)) drives_list.set_item_metadata(item_idx, - Actions.new(Callable(), enter_dir.bind(drive_path))) + Actions.new(Callable(), open_dir.bind(drive_path))) if current_dir == drive_path: drives_list.select(item_idx) drives_list.sort_items_by_text() @@ -191,7 +205,7 @@ func set_dir(dir: String) -> void: var item_idx := file_list.add_item(directory, folder_icon) var dir_path := current_dir.path_join(directory) file_list.set_item_metadata(item_idx, Actions.new( - enter_dir.bind(dir_path), unfocus_file, open_dir_context.bind(dir_path))) + open_dir.bind(dir_path), sync_to_selection, open_dir_context.bind(dir_path))) for file in files: if not file.get_extension() in extensions or\ @@ -200,27 +214,28 @@ func set_dir(dir: String) -> void: var item_idx := file_list.add_item(file, null) file_list.set_item_metadata(item_idx, Actions.new( - select_file, focus_file.bind(file), open_file_context.bind(file))) + select_files, sync_to_selection, open_file_context)) # If we don't await this stuff, sometimes the item_rect we get is all wrong. await file_list.draw await get_tree().process_frame _setup_file_images() -func set_file(file: String) -> void: - if mode == FileMode.SELECT: - if file.is_empty(): - special_button.disabled = true - special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW - else: - special_button.disabled = false - special_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND - if not file.is_empty() and not file.get_extension() in extensions and\ - extensions.size() == 1: - file += "." + extensions[0] - file_list.ensure_current_is_visible() - current_file = file - if not file.is_empty(): - file_field.text = file +func sync_file_field() -> void: + file_field.text = add_extension_if_missing(get_save_name()) + +func add_extension_if_missing(file_name: String) -> String: + if not file_name.is_empty() and not file_name.get_extension() in extensions and\ + extensions.size() >= 1: + return file_name + "." + extensions[0] + else: + return file_name + +func get_save_name() -> String: + var selected_file_paths := get_selected_file_paths() + if selected_file_paths.is_empty() or selected_file_paths[0].get_extension().is_empty(): + return default_saved_file + else: + return selected_file_paths[0].get_file() # For optimization, only generate the visible files' images. func _setup_file_images() -> void: @@ -254,25 +269,53 @@ func _setup_file_images() -> void: file_list.set_item_icon(item_idx, ImageTexture.create_from_image(img)) -func select_file() -> void: - if mode == FileMode.SAVE and FileAccess.file_exists(current_dir.path_join(current_file)): - var confirm_dialog := ConfirmDialogScene.instantiate() - HandlerGUI.add_dialog(confirm_dialog) - confirm_dialog.setup(Translator.translate("Alert!"), Translator.translate( - "A file named \"{file_name}\" already exists. Replacing will overwrite its contents!").format( - {"file_name": current_file}), Translator.translate("Replace"), - _on_replace_button_pressed) +func select_files() -> void: + if mode == FileMode.SAVE: + var save_name := get_save_name() + if FileAccess.file_exists(current_dir.path_join(save_name)): + var confirm_dialog := ConfirmDialogScene.instantiate() + HandlerGUI.add_dialog(confirm_dialog) + confirm_dialog.setup(Translator.translate("Alert!"), Translator.translate( + "A file named \"{file_name}\" already exists. Replacing will overwrite its contents!").format( + {"file_name": save_name}), Translator.translate("Replace"), + files_selected.emit.bind(PackedStringArray([current_dir.path_join(save_name)]))) + else: + files_selected.emit(PackedStringArray([current_dir.path_join(save_name)])) else: - file_selected.emit(current_dir.path_join(current_file)) + files_selected.emit(get_selected_file_paths()) -func focus_file(path: String) -> void: - set_file(path.get_file()) +func sync_to_selection() -> void: + file_list.ensure_current_is_visible() + if mode != FileMode.SAVE: + var paths := get_selected_file_paths() + if paths.is_empty(): + set_special_button_enabled(false) + else: + var has_folders := false + for path in paths: + if path.get_extension().is_empty(): + has_folders = true + break + set_special_button_enabled(not has_folders) + else: + sync_file_field() + +func set_special_button_enabled(enabled: bool) -> void: + if enabled: + special_button.disabled = false + special_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + else: + special_button.disabled = true + special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW -func unfocus_file() -> void: - set_file(default_file) +func get_selected_file_paths() -> PackedStringArray: + var selections := PackedStringArray() + for selected_idx in file_list.get_selected_items(): + selections.append(current_dir.path_join(file_list.get_item_text(selected_idx))) + return selections func copy_file_path() -> void: - DisplayServer.clipboard_set(current_dir.path_join(current_file)) + DisplayServer.clipboard_set(get_selected_file_paths()[0]) func create_folder() -> void: var create_folder_dialog := ChooseNameDialogScene.instantiate() @@ -304,6 +347,9 @@ func _on_create_folder_finished(text: String) -> void: func open_dir_context(dir: String) -> void: + if get_selected_file_paths().size() > 1: + return + var context_popup := ContextPopup.new() var btn_arr: Array[Button] = [ ContextPopup.create_shortcut_button("ui_accept", false, @@ -315,13 +361,22 @@ func open_dir_context(dir: String) -> void: var vp := get_viewport() HandlerGUI.popup_under_pos(context_popup, vp.get_mouse_position(), vp) -func open_file_context(file: String) -> void: - focus_file(file) +func open_file_context() -> void: + sync_to_selection() + var selected_file_paths := get_selected_file_paths() + if selected_file_paths.size() > 1: + # Return if any of the files is actually a folder. + for path in selected_file_paths: + if path.get_extension().is_empty(): + return + var btn_arr: Array[Button] = [ ContextPopup.create_shortcut_button("ui_accept", false, special_button.text, - load("res://assets/icons/OpenFile.svg")), - ContextPopup.create_button(Translator.translate("Copy path"), - copy_file_path, false, load("res://assets/icons/Copy.svg"))] + load("res://assets/icons/OpenFile.svg"))] + if selected_file_paths.size() == 1: + btn_arr.append(ContextPopup.create_button(Translator.translate("Copy path"), + copy_file_path, false, load("res://assets/icons/Copy.svg"))) + var context_popup := ContextPopup.new() context_popup.setup(btn_arr, true) var vp := get_viewport() @@ -329,12 +384,12 @@ func open_file_context(file: String) -> void: func _on_folder_up_button_pressed() -> void: - set_dir(current_dir.get_base_dir()) + open_dir(current_dir.get_base_dir()) func _on_file_list_empty_clicked(_at_position: Vector2, mouse_button_index: int) -> void: if mouse_button_index in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_RIGHT]: file_list.deselect_all() - unfocus_file() + sync_to_selection() if mouse_button_index == MOUSE_BUTTON_RIGHT and mode == FileMode.SAVE: var context_popup := ContextPopup.new() var btn_arr: Array[Button] = [ @@ -350,6 +405,10 @@ func _on_file_list_item_activated(index: int) -> void: func _on_file_list_item_selected(index: int) -> void: call_selection_callback(file_list.get_item_metadata(index)) +func _on_file_list_item_multi_selected(index: int, selected: bool) -> void: + if selected: + call_selection_callback(file_list.get_item_metadata(index)) + func _on_file_list_item_clicked(index: int, _at_position: Vector2, mouse_button_index: int) -> void: if mouse_button_index == MOUSE_BUTTON_RIGHT: @@ -375,16 +434,19 @@ func _on_search_button_toggled(toggled_on: bool) -> void: func _on_file_field_text_submitted(new_text: String) -> void: file_field.remove_theme_color_override("font_color") if new_text.is_valid_filename(): - current_file = new_text + default_saved_file = new_text else: - file_field.text = current_file + file_field.text = default_saved_file func _on_path_field_text_submitted(new_text: String) -> void: dir_cursor = DirAccess.open(new_text) if is_instance_valid(dir_cursor): - set_dir(new_text) + open_dir(new_text) else: - path_field.text = current_dir + sync_path_field() + +func sync_path_field() -> void: + path_field.text = Utils.simplify_file_path(current_dir) func _on_search_field_text_changed(new_text: String) -> void: search_text = new_text @@ -394,25 +456,15 @@ func _on_search_field_text_change_canceled() -> void: search_field.text = search_text func _on_file_field_text_changed(new_text: String) -> void: - var is_invalid_filename := not new_text.is_valid_filename() - if new_text.is_empty() or is_invalid_filename: - special_button.disabled = true - special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW - else: - special_button.disabled = false - special_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND - + var is_valid_filename := new_text.is_valid_filename() + set_special_button_enabled(not new_text.is_empty() and is_valid_filename) file_field.add_theme_color_override("font_color", - Configs.savedata.get_validity_color(is_invalid_filename)) + Configs.savedata.get_validity_color(not is_valid_filename)) func _on_file_field_text_change_canceled() -> void: file_field.remove_theme_color_override("font_color") -func _on_replace_button_pressed() -> void: - file_selected.emit(current_dir.path_join(current_file)) - - # Helpers func _init() -> void: diff --git a/src/ui_parts/good_file_dialog.tscn b/src/ui_parts/good_file_dialog.tscn index f50ecab..be1c14b 100644 --- a/src/ui_parts/good_file_dialog.tscn +++ b/src/ui_parts/good_file_dialog.tscn @@ -136,7 +136,6 @@ size_flags_horizontal = 6 focus_mode = 0 mouse_default_cursor_shape = 2 -[connection signal="pressed" from="VBoxContainer/TopBar/FolderUpButton" to="." method="_on_folder_up_button_pressed"] [connection signal="text_submitted" from="VBoxContainer/TopBar/PathField" to="." method="_on_path_field_text_submitted"] [connection signal="toggled" from="VBoxContainer/TopBar/ShowHiddenButton" to="." method="_on_show_hidden_button_toggled"] [connection signal="toggled" from="VBoxContainer/TopBar/SearchButton" to="." method="_on_search_button_toggled"] @@ -146,7 +145,6 @@ mouse_default_cursor_shape = 2 [connection signal="empty_clicked" from="VBoxContainer/MainContainer/FilePicker/VisualPicker/FileList" to="." method="_on_file_list_empty_clicked"] [connection signal="item_activated" from="VBoxContainer/MainContainer/FilePicker/VisualPicker/FileList" to="." method="_on_file_list_item_activated"] [connection signal="item_clicked" from="VBoxContainer/MainContainer/FilePicker/VisualPicker/FileList" to="." method="_on_file_list_item_clicked"] -[connection signal="item_selected" from="VBoxContainer/MainContainer/FilePicker/VisualPicker/FileList" to="." method="_on_file_list_item_selected"] [connection signal="text_change_canceled" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_change_canceled"] [connection signal="text_changed" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_changed"] [connection signal="text_submitted" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_submitted"] diff --git a/src/ui_parts/handles_manager.gd b/src/ui_parts/handles_manager.gd index ddd0ee9..328f60f 100644 --- a/src/ui_parts/handles_manager.gd +++ b/src/ui_parts/handles_manager.gd @@ -116,7 +116,7 @@ func update_handles() -> void: _handles_update_pending = false handles.clear() - for element in State.root_element.get_all_element_descendants(): + for element in State.root_element.get_all_valid_element_descendants(): match element.name: "circle": handles.append(XYHandle.new(element, "cx", "cy")) @@ -132,6 +132,8 @@ func update_handles() -> void: "line": handles.append(XYHandle.new(element, "x1", "y1")) handles.append(XYHandle.new(element, "x2", "y2")) + "use": + handles.append(XYHandle.new(element, "x", "y")) "polygon", "polyline": handles += generate_polyhandles(element) "path": @@ -195,7 +197,7 @@ func _draw() -> void: var hovered_multiline := PackedVector2Array() var hovered_selected_multiline := PackedVector2Array() - for element: Element in State.root_element.get_all_element_descendants(): + for element: Element in State.root_element.get_all_valid_element_descendants(): # Determine if the element is hovered/selected or has a hovered/selected parent. var element_hovered := State.is_hovered(element.xid, -1, true) var element_selected := State.is_selected(element.xid, -1, true) @@ -267,7 +269,7 @@ func _draw() -> void: var rx: float = element.get_rx() var ry: float = element.get_ry() var points := PackedVector2Array() - if rx == 0 and ry == 0: + if rx == 0 or ry == 0: # Basic rectangle. points = [Vector2(x, y), Vector2(x + rect_width, y), Vector2(x + rect_width, y + rect_height), @@ -546,7 +548,7 @@ func _draw() -> void: e1 = e2 t += PI/4 - if n != ceili(segments): + if n != ceili(segments) and not is_equal_approx(n, segments): t = theta1 + delta_theta var p2 := Utils.E(c, r, cosine, sine, t) var e2 := Utils.Et(r, cosine, sine, t) diff --git a/src/ui_parts/tab_bar.gd b/src/ui_parts/tab_bar.gd index 8e9b54a..c6ce523 100644 --- a/src/ui_parts/tab_bar.gd +++ b/src/ui_parts/tab_bar.gd @@ -10,6 +10,7 @@ const scroll_backwards_icon = preload("res://assets/icons/ScrollBackwards.svg") const DEFAULT_TAB_WIDTH = 140.0 const MIN_TAB_WIDTH = 70.0 const CLOSE_BUTTON_MARGIN = 2 +const SCROLL_SPEED = 10.0 var ci := get_canvas_item() @@ -98,10 +99,9 @@ func _draw() -> void: text_line.draw(ci, rect.position + Vector2(4, 3), text_color) var add_button_rect := get_add_button_rect() - if add_button_rect.has_area(): - var plus_icon_size := plus_icon.get_size() - draw_texture_rect(plus_icon, Rect2(add_button_rect.position +\ - (add_button_rect.size - plus_icon_size) / 2.0, plus_icon_size), false) + var plus_icon_size := plus_icon.get_size() + draw_texture_rect(plus_icon, Rect2(add_button_rect.position +\ + (add_button_rect.size - plus_icon_size) / 2.0, plus_icon_size), false) var scroll_backwards_rect := get_scroll_backwards_area_rect() if scroll_backwards_rect.has_area(): @@ -159,9 +159,9 @@ func _gui_input(event: InputEvent) -> void: if event is InputEventMouseButton: if event.is_pressed(): if event.button_index in [MOUSE_BUTTON_WHEEL_UP, MOUSE_BUTTON_WHEEL_LEFT]: - scroll_backwards() + scroll_backwards(event.factor) if event.button_index in [MOUSE_BUTTON_WHEEL_DOWN, MOUSE_BUTTON_WHEEL_RIGHT]: - scroll_forwards() + scroll_forwards(event.factor) elif event.button_index in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT]: var hovered_idx := get_hovered_index() if hovered_idx != -1: @@ -198,6 +198,18 @@ func _gui_input(event: InputEvent) -> void: var file_absent := not FileAccess.file_exists(new_active_tab.svg_file_path) var tab_count := Configs.savedata.get_tab_count() + var has_empty_tabs := false + for tab in Configs.savedata.get_tabs(): + if tab.is_empty(): + has_empty_tabs = true + break + + var has_saved_tabs := false + for tab in Configs.savedata.get_tabs(): + if tab.is_saved(): + has_saved_tabs = true + break + btn_arr.append(ContextPopup.create_shortcut_button_without_icon( "close_tab")) # TODO Unify into "Close multiple tabs" @@ -207,13 +219,17 @@ func _gui_input(event: InputEvent) -> void: "close_tabs_to_left", hovered_idx == 0)) btn_arr.append(ContextPopup.create_shortcut_button_without_icon( "close_tabs_to_right", hovered_idx == tab_count - 1)) - + btn_arr.append(ContextPopup.create_shortcut_button_without_icon( + "close_empty_tabs", not has_empty_tabs)) + btn_arr.append(ContextPopup.create_shortcut_button_without_icon( + "close_saved_tabs", not has_saved_tabs)) + btn_arr.append(ContextPopup.create_shortcut_button("open_externally", file_absent)) btn_arr.append(ContextPopup.create_shortcut_button("open_in_folder", file_absent)) var tab_popup := ContextPopup.new() - tab_popup.setup(btn_arr, true, -1, -1, PackedInt32Array([4])) + tab_popup.setup(btn_arr, true, -1, -1, PackedInt32Array([6])) if hovered_idx != -1: var tab_global_rect := get_tab_rect(hovered_idx) @@ -238,8 +254,8 @@ func _process(_delta: float) -> void: var mouse_pos := get_local_mouse_position() var scroll_forwards_area_rect := get_scroll_forwards_area_rect() if ((scrolling_forwards and scroll_forwards_area_rect.has_point(mouse_pos)) or\ - (mouse_pos.x > size.x - get_add_button_rect().size.x -\ - scroll_forwards_area_rect.size.x)) and scroll_forwards_area_rect.has_area(): + (mouse_pos.x > size.x - size.y - scroll_forwards_area_rect.size.x)) and\ + scroll_forwards_area_rect.has_area(): scroll_forwards() return @@ -264,18 +280,18 @@ func cleanup() -> void: queue_redraw() -func scroll_backwards() -> void: - set_scroll(current_scroll - 5.0) +func scroll_backwards(factor := 1.0) -> void: + set_scroll(current_scroll - SCROLL_SPEED * factor) -func scroll_forwards() -> void: - set_scroll(current_scroll + 5.0) +func scroll_forwards(factor := 1.0) -> void: + set_scroll(current_scroll + SCROLL_SPEED * factor) func scroll_to_active() -> void: var idx := Configs.savedata.get_active_tab_index() if\ State.transient_tab_path.is_empty() else Configs.savedata.get_tab_count() - set_scroll(clampf(current_scroll, MIN_TAB_WIDTH * (idx + 1) -\ - size.x + get_add_button_rect().size.x + get_scroll_forwards_area_rect().size.x +\ - get_scroll_backwards_area_rect().size.x, MIN_TAB_WIDTH * idx)) + set_scroll(clampf(current_scroll, MIN_TAB_WIDTH * (idx + 1) - size.x + size.y +\ + get_scroll_forwards_area_rect().size.x + get_scroll_backwards_area_rect().size.x, + MIN_TAB_WIDTH * idx)) func set_scroll(new_value: float) -> void: if get_scroll_limit() < 0: @@ -295,14 +311,13 @@ func get_proper_tab_count() -> int: func get_tab_rect(idx: int) -> Rect2: # Things that can take space. - var add_button_width := get_add_button_rect().size.x var scroll_backwards_button_width := get_scroll_backwards_area_rect().size.x var scroll_forwards_button_width := get_scroll_forwards_area_rect().size.x var left_limit := scroll_backwards_button_width - var right_limit := size.x - add_button_width - scroll_forwards_button_width + var right_limit := size.x - size.y - scroll_forwards_button_width - var tab_width := clampf((size.x - add_button_width - scroll_backwards_button_width -\ + var tab_width := clampf((size.x - size.y - scroll_backwards_button_width -\ scroll_forwards_button_width) / get_proper_tab_count(), MIN_TAB_WIDTH, DEFAULT_TAB_WIDTH) var unclamped_tab_start := tab_width * idx - current_scroll + left_limit @@ -324,23 +339,20 @@ func get_close_button_rect() -> Rect2: return Rect2(left_coords, CLOSE_BUTTON_MARGIN, side, side) func get_add_button_rect() -> Rect2: - var tab_count := get_proper_tab_count() - if tab_count >= SaveData.MAX_TABS: - return Rect2() - return Rect2(minf(DEFAULT_TAB_WIDTH * tab_count, size.x - size.y), 0, size.y, size.y) + return Rect2(minf(DEFAULT_TAB_WIDTH * get_proper_tab_count(), size.x - size.y), 0, + size.y, size.y) func get_scroll_forwards_area_rect() -> Rect2: - var add_button_width := get_add_button_rect().size.x - if size.x - add_button_width > get_proper_tab_count() * MIN_TAB_WIDTH: + if size.x - size.y > get_proper_tab_count() * MIN_TAB_WIDTH: return Rect2() var width := size.y / 1.5 - return Rect2(size.x - add_button_width - width, 0, width, size.y) + return Rect2(size.x - size.y - width, 0, width, size.y) func is_scroll_forwards_disabled() -> bool: return current_scroll >= get_scroll_limit() func get_scroll_backwards_area_rect() -> Rect2: - if size.x - get_add_button_rect().size.x > get_proper_tab_count() * MIN_TAB_WIDTH: + if size.x - size.y > get_proper_tab_count() * MIN_TAB_WIDTH: return Rect2() return Rect2(0, 0, size.y / 1.5, size.y) @@ -348,11 +360,10 @@ func is_scroll_backwards_disabled() -> bool: return current_scroll <= 0.0 func get_scroll_limit() -> float: - var add_button_width := get_add_button_rect().size.x var scroll_backwards_button_width := get_scroll_backwards_area_rect().size.x var scroll_forwards_button_width := get_scroll_forwards_area_rect().size.x - var available_area := size.x - add_button_width - scroll_backwards_button_width -\ + var available_area := size.x - size.y - scroll_backwards_button_width -\ scroll_forwards_button_width return clampf(available_area / get_proper_tab_count(), MIN_TAB_WIDTH, DEFAULT_TAB_WIDTH) * get_proper_tab_count() - available_area @@ -386,17 +397,16 @@ func activate() -> void: ) var add_rect := get_add_button_rect() - if add_rect.has_area(): - var add_button := Button.new() - add_button.theme_type_variation = "FlatButton" - add_button.focus_mode = Control.FOCUS_NONE - add_button.position = add_rect.position - add_button.size = add_rect.size - add_button.mouse_filter = Control.MOUSE_FILTER_PASS - add_button.tooltip_text = Translator.translate("Create a new tab") - add_child(add_button) - active_controls.append(add_button) - add_button.pressed.connect(Configs.savedata.add_empty_tab) + var add_button := Button.new() + add_button.theme_type_variation = "FlatButton" + add_button.focus_mode = Control.FOCUS_NONE + add_button.position = add_rect.position + add_button.size = add_rect.size + add_button.mouse_filter = Control.MOUSE_FILTER_PASS + add_button.tooltip_text = Translator.translate("Create a new tab") + add_child(add_button) + active_controls.append(add_button) + add_button.pressed.connect(Configs.savedata.add_empty_tab) func _get_tooltip(at_position: Vector2) -> String: @@ -483,12 +493,11 @@ class TabDragData: index = new_index func get_drop_index_at(pos: Vector2) -> int: - var add_button_width := get_add_button_rect().size.x var scroll_backwards_button_width := get_scroll_backwards_area_rect().size.x var scroll_forwards_button_width := get_scroll_forwards_area_rect().size.x if pos.x < scroll_backwards_button_width or\ - pos.x > size.x - scroll_forwards_button_width - add_button_width: + pos.x > size.x - scroll_forwards_button_width - size.y: return -1 var first_tab_with_area := 0 @@ -497,7 +506,7 @@ func get_drop_index_at(pos: Vector2) -> int: first_tab_with_area = idx break - var tab_width := clampf((size.x - add_button_width - scroll_backwards_button_width -\ + var tab_width := clampf((size.x - size.y - scroll_backwards_button_width -\ scroll_forwards_button_width) / get_proper_tab_count(), MIN_TAB_WIDTH, DEFAULT_TAB_WIDTH) diff --git a/src/ui_widgets/alert_dialog.tscn b/src/ui_widgets/alert_dialog.tscn index 5f89e32..77c05f4 100644 --- a/src/ui_widgets/alert_dialog.tscn +++ b/src/ui_widgets/alert_dialog.tscn @@ -33,9 +33,8 @@ theme_override_font_sizes/font_size = 16 horizontal_alignment = 1 [node name="Label" type="RichTextLabel" parent="MainContainer/TextContainer"] -custom_minimum_size = Vector2(180, 0) +custom_minimum_size = Vector2(200, 0) layout_mode = 2 -theme_override_font_sizes/normal_font_size = 12 fit_content = true [node name="OKButton" type="Button" parent="MainContainer"] diff --git a/src/ui_widgets/color_field_popup.gd b/src/ui_widgets/color_field_popup.gd index c28ed5f..3008bae 100644 --- a/src/ui_widgets/color_field_popup.gd +++ b/src/ui_widgets/color_field_popup.gd @@ -82,7 +82,7 @@ func update_palettes(search_text := "") -> void: reserved_colors.append("currentColor") reserved_color_names.append("Current color") if show_url: - for element in State.root_element.get_all_element_descendants(): + for element in State.root_element.get_all_valid_element_descendants(): if element.has_attribute("id"): if element is ElementLinearGradient: reserved_color_names.append("Linear gradient") diff --git a/src/ui_widgets/confirm_dialog.tscn b/src/ui_widgets/confirm_dialog.tscn index 5d8981e..3197974 100644 --- a/src/ui_widgets/confirm_dialog.tscn +++ b/src/ui_widgets/confirm_dialog.tscn @@ -33,9 +33,8 @@ theme_override_font_sizes/font_size = 16 horizontal_alignment = 1 [node name="Label" type="RichTextLabel" parent="MainContainer/TextContainer"] -custom_minimum_size = Vector2(300, 0) +custom_minimum_size = Vector2(320, 0) layout_mode = 2 -theme_override_font_sizes/normal_font_size = 12 fit_content = true [node name="ButtonContainer" type="HBoxContainer" parent="MainContainer"] diff --git a/src/ui_widgets/element_frame.gd b/src/ui_widgets/element_frame.gd index 2f815f4..c430aa5 100644 --- a/src/ui_widgets/element_frame.gd +++ b/src/ui_widgets/element_frame.gd @@ -10,6 +10,7 @@ const element_content_types: Dictionary[String, PackedScene] = { "ellipse": preload("res://src/ui_widgets/element_content_basic_shape.tscn"), "rect": preload("res://src/ui_widgets/element_content_basic_shape.tscn"), "line": preload("res://src/ui_widgets/element_content_basic_shape.tscn"), + "use": preload("res://src/ui_widgets/element_content_basic_shape.tscn"), "stop": preload("res://src/ui_widgets/element_content_stop.tscn"), "g": preload("res://src/ui_widgets/element_content_g.tscn"), "svg": preload("res://src/ui_widgets/element_content_g.tscn"), diff --git a/src/ui_widgets/href_field.gd b/src/ui_widgets/href_field.gd new file mode 100644 index 0000000..9ffd918 --- /dev/null +++ b/src/ui_widgets/href_field.gd @@ -0,0 +1,46 @@ +extends BetterLineEdit + +var element: Element +const attribute_name = "href" # Never propagates. + +func set_value(new_value: String, save := false) -> void: + element.set_attribute(attribute_name, new_value) + sync() + if save: + State.queue_svg_save() + + +func _ready() -> void: + Configs.basic_colors_changed.connect(sync) + sync() + element.attribute_changed.connect(_on_element_attribute_changed) + text_changed.connect(_on_text_changed) + text_submitted.connect(_on_text_submitted) + text_change_canceled.connect(sync) + focus_entered.connect(_on_focus_entered) + tooltip_text = attribute_name + +func _on_element_attribute_changed(attribute_changed: String) -> void: + if attribute_name == attribute_changed: + sync() + +func _on_focus_entered() -> void: + remove_theme_color_override("font_color") + +func _on_text_submitted(new_text: String) -> void: + if new_text.is_empty() or\ + AttributeHref.get_validity(new_text) != Attribute.NameValidityLevel.INVALID: + set_value(new_text, true) + else: + sync() + +func _on_text_changed(new_text: String) -> void: + var validity_level := AttributeHref.get_validity(new_text) + var font_color := Configs.savedata.get_validity_color( + validity_level == Attribute.NameValidityLevel.INVALID, + validity_level == Attribute.NameValidityLevel.INVALID_XML_NAMETOKEN) + add_theme_color_override("font_color", font_color) + +func sync() -> void: + text = element.get_attribute_value(attribute_name) + remove_theme_color_override("font_color") diff --git a/src/ui_widgets/href_field.gd.uid b/src/ui_widgets/href_field.gd.uid new file mode 100644 index 0000000..253fd37 --- /dev/null +++ b/src/ui_widgets/href_field.gd.uid @@ -0,0 +1 @@ +uid://c8t5rw00mtbbs diff --git a/src/ui_widgets/href_field.tscn b/src/ui_widgets/href_field.tscn new file mode 100644 index 0000000..c8a5a7c --- /dev/null +++ b/src/ui_widgets/href_field.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://cs6rpacldm1im"] + +[ext_resource type="Script" uid="uid://c8t5rw00mtbbs" path="res://src/ui_widgets/href_field.gd" id="1_y6vhr"] + +[node name="HrefField" type="LineEdit"] +custom_minimum_size = Vector2(54, 22) +offset_right = 35.8125 +offset_bottom = 21.0 +size_flags_horizontal = 0 +size_flags_vertical = 0 +script = ExtResource("1_y6vhr") +mono_font_tooltip = true diff --git a/src/ui_widgets/id_field.gd b/src/ui_widgets/id_field.gd index ee81fd5..3752afe 100644 --- a/src/ui_widgets/id_field.gd +++ b/src/ui_widgets/id_field.gd @@ -30,7 +30,7 @@ func _on_focus_entered() -> void: func _on_text_submitted(new_text: String) -> void: if new_text.is_empty() or\ - AttributeID.get_validity(new_text) != AttributeID.ValidityLevel.INVALID: + AttributeID.get_validity(new_text) != Attribute.NameValidityLevel.INVALID: set_value(new_text, true) else: sync() @@ -38,8 +38,8 @@ func _on_text_submitted(new_text: String) -> void: func _on_text_changed(new_text: String) -> void: var validity_level := AttributeID.get_validity(new_text) var font_color := Configs.savedata.get_validity_color( - validity_level == AttributeID.ValidityLevel.INVALID, - validity_level == AttributeID.ValidityLevel.INVALID_XML_NAMETOKEN) + validity_level == Attribute.NameValidityLevel.INVALID, + validity_level == Attribute.NameValidityLevel.INVALID_XML_NAMETOKEN) add_theme_color_override("font_color", font_color) func sync() -> void: diff --git a/src/ui_widgets/options_dialog.gd b/src/ui_widgets/options_dialog.gd index ed65be7..6ef1d36 100644 --- a/src/ui_widgets/options_dialog.gd +++ b/src/ui_widgets/options_dialog.gd @@ -1,22 +1,61 @@ +# A more complex dialog, with the ability to have any number of arbitrary options, +# a list, and a checkbox. extends PanelContainer @onready var title_label: Label = $MainContainer/TextContainer/Title @onready var label: RichTextLabel = $MainContainer/TextContainer/Label @onready var options_container: HBoxContainer = $MainContainer/OptionsContainer +@onready var list_panel: PanelContainer = %ListPanel +@onready var list_label: Label = %ListLabel +@onready var list_scroll_container: ScrollContainer = %ListPanel/ListScrollContainer +@onready var checkbox: CheckBox = $MainContainer/TextContainer/CheckBox -func setup(title: String, message: String) -> void: +func setup(title: String, message: String, list := PackedStringArray(), +checkbox_text := "", ) -> void: label.text = message title_label.text = title + if not list.is_empty(): + list_panel.show() + list_label.text = "\n".join(list) + if list.size() == 2: + list_scroll_container.custom_minimum_size.y = 41 + elif list.size() == 3: + list_scroll_container.custom_minimum_size.y = 63 + if not checkbox_text.is_empty(): + checkbox.show() + checkbox.text = checkbox_text + +func set_text_width(width: float) -> void: + label.custom_minimum_size.x = width func add_option(action_text: String, action: Callable, focused := false, -free_on_click := true) -> void: +free_on_click := true, action_when_checkbox_checked := Callable(), +disabled_when_checkbox_pressed := false, grab_focus_when_checkbox_pressed := false) -> void: var button := Button.new() button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND button.text = action_text button.size_flags_horizontal = Control.SIZE_EXPAND | Control.SIZE_SHRINK_CENTER - button.pressed.connect(action) + + if action_when_checkbox_checked.is_valid(): + button.pressed.connect(func() -> void: + if checkbox.button_pressed: + action_when_checkbox_checked.call() + else: + action.call() + ) + else: + button.pressed.connect(action) + if free_on_click: button.pressed.connect(queue_free) + if disabled_when_checkbox_pressed: + checkbox.toggled.connect(get_button_disabled_callable(button)) + if grab_focus_when_checkbox_pressed: + checkbox.toggled.connect( + func(pressed: bool) -> void: + if pressed: + button.grab_focus() + ) options_container.add_child(button) if focused: button.grab_focus() @@ -28,4 +67,17 @@ func add_cancel_option() -> void: button.text = Translator.translate("Cancel") button.size_flags_horizontal = Control.SIZE_EXPAND | Control.SIZE_SHRINK_CENTER button.pressed.connect(queue_free) + checkbox.toggled.connect(get_button_disabled_callable(button)) options_container.add_child(button) + + +func get_button_disabled_callable(button: Button) -> Callable: + return func(disabled: bool) -> void: + if disabled: + button.disabled = true + button.mouse_default_cursor_shape = Control.CURSOR_ARROW + button.focus_mode = Control.FOCUS_NONE + else: + button.disabled = false + button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + button.focus_mode = Control.FOCUS_ALL diff --git a/src/ui_widgets/options_dialog.tscn b/src/ui_widgets/options_dialog.tscn index b76eb4d..a815513 100644 --- a/src/ui_widgets/options_dialog.tscn +++ b/src/ui_widgets/options_dialog.tscn @@ -33,11 +33,34 @@ theme_override_font_sizes/font_size = 16 horizontal_alignment = 1 [node name="Label" type="RichTextLabel" parent="MainContainer/TextContainer"] -custom_minimum_size = Vector2(300, 0) +custom_minimum_size = Vector2(320, 0) layout_mode = 2 -theme_override_font_sizes/normal_font_size = 12 fit_content = true +[node name="ListPanel" type="PanelContainer" parent="MainContainer/TextContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="ListScrollContainer" type="ScrollContainer" parent="MainContainer/TextContainer/ListPanel"] +custom_minimum_size = Vector2(0, 84) +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="MainContainer/TextContainer/ListPanel/ListScrollContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 4 + +[node name="ListLabel" type="Label" parent="MainContainer/TextContainer/ListPanel/ListScrollContainer/MarginContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_vertical = 3 + +[node name="CheckBox" type="CheckBox" parent="MainContainer/TextContainer"] +visible = false +layout_mode = 2 +focus_mode = 0 +mouse_default_cursor_shape = 2 + [node name="OptionsContainer" type="HBoxContainer" parent="MainContainer"] layout_mode = 2 alignment = 1 diff --git a/src/utils/AttributeFieldBuilder.gd b/src/utils/AttributeFieldBuilder.gd index 76dc437..dbed640 100644 --- a/src/utils/AttributeFieldBuilder.gd +++ b/src/utils/AttributeFieldBuilder.gd @@ -6,11 +6,13 @@ const NumberSliderScene = preload("res://src/ui_widgets/number_field_with_slider const ColorFieldScene = preload("res://src/ui_widgets/color_field.tscn") const EnumFieldScene = preload("res://src/ui_widgets/enum_field.tscn") const IdFieldScene = preload("res://src/ui_widgets/id_field.tscn") +const HrefFieldScene = preload("res://src/ui_widgets/href_field.tscn") const UnrecognizedFieldScene = preload("res://src/ui_widgets/unrecognized_field.tscn") static func create(attribute: String, element: Element) -> Control: match DB.get_attribute_type(attribute): DB.AttributeType.ID: return _generate_no_name(IdFieldScene, element) + DB.AttributeType.HREF: return _generate_no_name(HrefFieldScene, element) DB.AttributeType.TRANSFORM_LIST: return _generate(TransformFieldScene, element, attribute) DB.AttributeType.COLOR: return _generate(ColorFieldScene, element, attribute) DB.AttributeType.ENUM: return _generate(EnumFieldScene, element, attribute) diff --git a/src/utils/FileUtils.gd b/src/utils/FileUtils.gd index 3f27580..af03f1a 100644 --- a/src/utils/FileUtils.gd +++ b/src/utils/FileUtils.gd @@ -2,7 +2,7 @@ class_name FileUtils extends RefCounted enum FileState {SAME, DIFFERENT, DOES_NOT_EXIST} -enum TabCloseMode {SINGLE, TO_LEFT, TO_RIGHT, ALL_OTHERS} +enum TabCloseMode {SINGLE, ALL_OTHERS, TO_LEFT, TO_RIGHT, EMPTY, SAVED} const GoodFileDialog = preload("res://src/ui_parts/good_file_dialog.gd") @@ -16,8 +16,8 @@ static func reset_svg() -> void: if FileAccess.file_exists(file_path): State.apply_svg_text(FileAccess.get_file_as_string(file_path)) -static func apply_svg_from_path(path: String) -> void: - _finish_file_import(path, _apply_svg, PackedStringArray(["svg"])) +static func apply_svgs_from_paths(paths: PackedStringArray) -> void: + _start_file_import_process(paths, _apply_svg, PackedStringArray(["svg"])) static func compare_svg_to_disk_contents() -> FileState: var content := FileAccess.get_file_as_string( @@ -77,8 +77,8 @@ static func open_export_dialog(export_data: ImageExportData, final_callback := C PackedStringArray(["*." + export_data.format]), native_callback) else: var non_native_callback :=\ - func(path: String) -> void: - _finish_export(path, export_data) + func(paths: PackedStringArray) -> void: + _finish_export(paths[0], export_data) if final_callback.is_valid(): final_callback.call() @@ -86,7 +86,7 @@ static func open_export_dialog(export_data: ImageExportData, final_callback := C export_dialog.setup(Configs.savedata.get_active_tab_dir(), _choose_file_name(), GoodFileDialog.FileMode.SAVE, PackedStringArray([export_data.format])) HandlerGUI.add_menu(export_dialog) - export_dialog.file_selected.connect(non_native_callback) + export_dialog.files_selected.connect(non_native_callback) static func open_xml_export_dialog(xml: String, file_name: String) -> void: if not OS.request_permissions(): @@ -110,8 +110,8 @@ static func open_xml_export_dialog(xml: String, file_name: String) -> void: export_dialog.setup(Configs.savedata.get_last_dir(), file_name, GoodFileDialog.FileMode.SAVE, PackedStringArray(["xml"])) HandlerGUI.add_menu(export_dialog) - export_dialog.file_selected.connect( - func(path: String) -> void: _finish_xml_export(path, xml)) + export_dialog.files_selected.connect( + func(paths: PackedStringArray) -> void: _finish_xml_export(paths[0], xml)) static func _finish_export(file_path: String, export_data: ImageExportData) -> void: if file_path.get_extension().is_empty(): @@ -138,7 +138,6 @@ static func _finish_xml_export(file_path: String, xml: String) -> void: Configs.savedata.add_recent_dir(file_path.get_base_dir()) FileAccess.open(file_path, FileAccess.WRITE).store_string(xml) - HandlerGUI.remove_all_menus() static func _finish_reference_load(data: Variant, file_path: String) -> void: var img := Image.new() @@ -163,7 +162,7 @@ static func _choose_file_name() -> String: # No need for completion callback here yet. static func open_svg_import_dialog() -> void: - _open_import_dialog(PackedStringArray(["svg"]), _apply_svg) + _open_import_dialog(PackedStringArray(["svg"]), _apply_svg, true) static func open_image_import_dialog() -> void: _open_import_dialog(PackedStringArray(["png", "jpg", "jpeg", "webp", "svg"]), @@ -172,9 +171,10 @@ static func open_image_import_dialog() -> void: static func open_xml_import_dialog(completion_callback: Callable) -> void: _open_import_dialog(PackedStringArray(["xml"]), completion_callback) -# On web, the completion callback can't use the full file path, + +# On web, the completion callback can't use the full file path. static func _open_import_dialog(extensions: PackedStringArray, -completion_callback: Callable) -> void: +completion_callback: Callable, multi_select := false) -> void: if not OS.request_permissions(): return var extensions_with_dots := PackedStringArray() @@ -192,53 +192,119 @@ completion_callback: Callable) -> void: var native_callback :=\ func(has_selected: bool, files: PackedStringArray, _filter_idx: int) -> void: if has_selected: - _finish_file_import(files[0], completion_callback, extensions) + _start_file_import_process(files, completion_callback, extensions) DisplayServer.file_dialog_show( - TranslationUtils.get_file_dialog_select_mode_title_text(extensions), - Configs.savedata.get_last_dir(), "", false, + TranslationUtils.get_file_dialog_select_mode_title_text(multi_select, + extensions), Configs.savedata.get_last_dir(), "", false, + DisplayServer.FILE_DIALOG_MODE_OPEN_FILES if multi_select else\ DisplayServer.FILE_DIALOG_MODE_OPEN_FILE, filters, native_callback) else: var import_dialog := GoodFileDialogScene.instantiate() import_dialog.setup(Configs.savedata.get_last_dir(), "", + GoodFileDialog.FileMode.MULTI_SELECT if multi_select else\ GoodFileDialog.FileMode.SELECT, extensions) HandlerGUI.add_menu(import_dialog) - import_dialog.file_selected.connect( - func(path: String) -> void: - _finish_file_import(path, completion_callback, extensions) + import_dialog.files_selected.connect( + func(paths: PackedStringArray) -> void: + _start_file_import_process(paths, completion_callback, extensions) ) -static func _finish_file_import(file_path: String, completion_callback: Callable, -allowed_extensions: PackedStringArray) -> Error: +static func _start_file_import_process(file_paths: PackedStringArray, +completion_callback: Callable, allowed_extensions: PackedStringArray) -> void: + var incorrect_extension_file_paths := PackedStringArray() + for i in range(file_paths.size() - 1, -1, -1): + if not file_paths[i].get_extension() in allowed_extensions: + incorrect_extension_file_paths.append(Utils.simplify_file_path(file_paths[i])) + file_paths.remove_at(i) + + var proceed_callback := _file_import_proceed.bind(file_paths, completion_callback) + + if not incorrect_extension_file_paths.is_empty(): + var error_text := TranslationUtils.get_extension_alert_text(allowed_extensions) + "\n" + var passed_list := PackedStringArray() # Only pass if there are more than two. + if incorrect_extension_file_paths.size() >= 2: + incorrect_extension_file_paths.reverse() + error_text += Translator.translate("The following files were discarded:") + passed_list = incorrect_extension_file_paths + else: + error_text += Translator.translate("{file_path} was discarded.").format( + {"file_path": incorrect_extension_file_paths[0]}) + + var options_dialog := OptionsDialogScene.instantiate() + HandlerGUI.add_dialog(options_dialog) + options_dialog.set_text_width(360.0) + options_dialog.setup(Translator.translate("Discarded files"), error_text, passed_list) + options_dialog.add_cancel_option() + if not file_paths.is_empty(): + options_dialog.add_option(Translator.translate("Proceed"), proceed_callback, true) + return + + proceed_callback.call() + +static func _file_import_proceed(file_paths: PackedStringArray, +completion_callback: Callable, show_file_missing_alert := true) -> void: + var file_path := file_paths[0] + var preserved_file_paths := file_paths.duplicate() + file_paths.remove_at(0) + var proceed_callback := _file_import_proceed.bind(file_paths, completion_callback, + show_file_missing_alert) + var retry_callback := _file_import_proceed.bind(preserved_file_paths, + completion_callback) var file := FileAccess.open(file_path, FileAccess.READ) - var error := "" - var file_extension := file_path.get_extension() + # If it's impossible to somehow import files from multiple dirs at the same time, + # this can be moved to the initial processing. Configs.savedata.add_recent_dir(file_path.get_base_dir()) - if not file_extension in allowed_extensions: - error = TranslationUtils.get_bad_extension_alert_text(file_extension, - allowed_extensions) - elif !is_instance_valid(file): - error = Translator.translate("The file couldn't be opened.") - if not FileAccess.file_exists(file_path): - error += "\n" + Translator.translate("Check if the file still exists in the selected file path.") - - if not error.is_empty(): - var alert_dialog := AlertDialogScene.instantiate() - HandlerGUI.add_dialog(alert_dialog) - alert_dialog.setup(error) - return ERR_FILE_CANT_OPEN + # If the file alert is shown, the buttons decide what happens next, so we exit early + # to avoid any file operations. If the file alert is not shown, we should call + # proceed_callback automatically and still exit early. + if not is_instance_valid(file): + if show_file_missing_alert: + var options_dialog := OptionsDialogScene.instantiate() + HandlerGUI.add_dialog(options_dialog) + var error := Translator.translate("{file_path} couldn't be opened.").format( + {"file_path": Utils.simplify_file_path(file_path)}) + if not FileAccess.file_exists(file_path): + error += "\n" + Translator.translate("Check if the file still exists in the selected file path.") + if not file_paths.is_empty(): + error += "\n" + Translator.translate("Proceed with importing the rest of the files?") + + if file_paths.is_empty(): + options_dialog.setup(Translator.translate("Alert!"), error) + else: + options_dialog.setup(Translator.translate("Alert!"), error, PackedStringArray(), + Translator.translate("Proceed for all files that can't be opened")) + + options_dialog.add_cancel_option() + options_dialog.add_option(Translator.translate("Retry"), retry_callback, false, + true, Callable(), true) + if not file_paths.is_empty(): + options_dialog.add_option(Translator.translate("Proceed"), proceed_callback, + true, true, _file_import_proceed.bind(file_paths, completion_callback, + false), false, true) + return + else: + if not file_paths.is_empty(): + proceed_callback.call() + return - # The XML callbacks happen to not need the file path. - match file_extension: - "svg": completion_callback.call(file.get_as_text(), file_path) + # The XML callbacks currently happen to not need the file path. + # The SVG callback used currently can popup extra dialogs, so they need the callable. + match file_path.get_extension(): + "svg": + if file_paths.is_empty(): + completion_callback.call(file.get_as_text(), file_path) + else: + completion_callback.call(file.get_as_text(), file_path, proceed_callback, + file_paths.is_empty()) "xml": completion_callback.call(file.get_as_text()) _: completion_callback.call(file.get_buffer(file.get_length()), file_path) - return OK -static func _apply_svg(data: Variant, file_path: String) -> void: +static func _apply_svg(data: Variant, file_path: String, proceed_callback := Callable(), +is_last_file := true) -> void: var tab_exists := false for tab in Configs.savedata.get_tabs(): if tab.svg_file_path == file_path: @@ -248,21 +314,26 @@ static func _apply_svg(data: Variant, file_path: String) -> void: if tab_exists: Configs.savedata.add_tab_with_path(file_path) var alert_message := Translator.translate( - "The imported file is already being edited inside GodSVG.") + "{file_path} is already being edited inside GodSVG.").format( + {"file_path": Utils.simplify_file_path(file_path)}) if compare_svg_to_disk_contents() == FileState.DIFFERENT: alert_message += "\n\n" + Translator.translate( "If you want to revert your edits since the last save, use {reset_svg}.").format( {"reset_svg": TranslationUtils.get_action_description("reset_svg")}) - var alert_dialog := AlertDialogScene.instantiate() - HandlerGUI.add_menu(alert_dialog) - alert_dialog.setup(alert_message) + var options_dialog := OptionsDialogScene.instantiate() + HandlerGUI.add_menu(options_dialog) + options_dialog.setup(Translator.translate("Alert!"), alert_message) + if is_last_file: + options_dialog.add_option("OK") + else: + options_dialog.add_cancel_option() + options_dialog.add_option("Proceed", proceed_callback, true) return # If the active tab is empty, replace it. Otherwise make it a new transient tab. - # If there are already too many tabs, do nothing. + var warning_panel := ImportWarningMenuScene.instantiate() if Configs.savedata.get_active_tab().empty_unsaved: - var warning_panel := ImportWarningMenuScene.instantiate() var tab_index := Configs.savedata.get_active_tab_index() Configs.savedata.add_tab_with_path(file_path) Configs.savedata.remove_tab(tab_index) @@ -270,16 +341,16 @@ static func _apply_svg(data: Variant, file_path: String) -> void: warning_panel.canceled.connect(_on_import_panel_canceled_empty_tab_scenario) warning_panel.imported.connect(_on_import_panel_accepted_empty_tab_scenario.bind( data)) - warning_panel.set_svg(data) - HandlerGUI.add_menu(warning_panel) - elif Configs.savedata.get_tab_count() < SaveData.MAX_TABS: - var warning_panel := ImportWarningMenuScene.instantiate() + else: State.transient_tab_path = file_path warning_panel.canceled.connect(_on_import_panel_canceled_transient_scenario) warning_panel.imported.connect(_on_import_panel_accepted_transient_scenario.bind( file_path, data)) - warning_panel.set_svg(data) - HandlerGUI.add_menu(warning_panel) + if not is_last_file: + warning_panel.canceled.connect(proceed_callback) + warning_panel.imported.connect(proceed_callback) + warning_panel.set_svg(data) + HandlerGUI.add_menu(warning_panel) static func _on_import_panel_canceled_empty_tab_scenario() -> void: var tab_index := Configs.savedata.get_active_tab_index() @@ -325,6 +396,20 @@ static func close_tabs(initial_idx: int, tab_close_mode := TabCloseMode.SINGLE) indices.append(0) for i in Configs.savedata.get_tab_count() - initial_idx - 1: indices.append(1) + TabCloseMode.EMPTY: + var idx_to_append := 0 + for tab in Configs.savedata.get_tabs(): + if tab.is_empty(): + indices.append(idx_to_append) + else: + idx_to_append += 1 + TabCloseMode.SAVED: + var idx_to_append := 0 + for tab in Configs.savedata.get_tabs(): + if tab.is_saved(): + indices.append(idx_to_append) + else: + idx_to_append += 1 _close_tabs_internal(indices) static func _close_tabs_internal(indices: Array[int]) -> void: @@ -415,8 +500,7 @@ completion_callback: Callable) -> void: if not extension in allowed_extensions: var alert_dialog := AlertDialogScene.instantiate() HandlerGUI.add_dialog(alert_dialog) - alert_dialog.setup(TranslationUtils.get_bad_extension_alert_text(extension, - allowed_extensions)) + alert_dialog.setup(TranslationUtils.get_extension_alert_text(allowed_extensions)) else: completion_callback.call(file_data, file_name) diff --git a/src/utils/ShortcutUtils.gd b/src/utils/ShortcutUtils.gd index 84e4c0a..4c3b654 100644 --- a/src/utils/ShortcutUtils.gd +++ b/src/utils/ShortcutUtils.gd @@ -12,9 +12,9 @@ const EFFECT_ACTIONS: PackedStringArray = ["view_show_grid", "view_show_handles" # Requires there being no popups either. const EDITOR_ACTIONS: 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", "debug"] + "close_tab", "close_all_other_tabs", "close_tabs_to_left", "close_tabs_to_right", + "close_empty_tabs", "close_saved_tabs", "new_tab", "select_next_tab", + "select_previous_tab", "copy_svg_text", "optimize", "reset_svg", "debug"] # Requires no drag-and-drop actions ongoing. const PRISTINE_ACTIONS: PackedStringArray = ["ui_undo", "ui_redo", "ui_cancel", "delete", @@ -29,9 +29,11 @@ const _action_categories_dict: Dictionary[String, Dictionary] = { "save": true, "save_as": true, "close_tab": true, + "close_all_other_tabs": true, "close_tabs_to_left": true, "close_tabs_to_right": true, - "close_all_other_tabs": true, + "close_empty_tabs": true, + "close_saved_tabs": true, "new_tab": true, "select_next_tab": true, "select_previous_tab": true, diff --git a/src/utils/TranslationUtils.gd b/src/utils/TranslationUtils.gd index 671737a..34ee248 100644 --- a/src/utils/TranslationUtils.gd +++ b/src/utils/TranslationUtils.gd @@ -26,6 +26,8 @@ static func get_action_description(action_name: String, for_button := false) -> "close_tabs_to_left": return Translator.translate("Close tabs to the left") "close_tabs_to_right": return Translator.translate("Close tabs to the right") "close_all_other_tabs": return Translator.translate("Close all other tabs") + "close_empty_tabs": return Translator.translate("Close empty tabs") + "close_saved_tabs": return Translator.translate("Close saved tabs") "new_tab": return Translator.translate("Create tab") if\ for_button else Translator.translate("Create a new tab") "select_next_tab": return Translator.translate("Select the next tab") @@ -133,24 +135,27 @@ static func get_layout_part_name(layout_part: Utils.LayoutPart) -> String: _: return "" -static func get_bad_extension_alert_text(extension: String, -allowed_extensions: PackedStringArray) -> String: +static func get_extension_alert_text(allowed_extensions: PackedStringArray) -> String: + for i in allowed_extensions.size(): + allowed_extensions[i] = get_extension_readable_name(allowed_extensions[i]) var extension_list := ", ".join(allowed_extensions) - if extension.is_empty(): - return Translator.translate( - "The file extension is empty. Only {extension_list} files are supported.").format( - {"extension_list": extension_list}) return Translator.translate( - "The file extension {extension} is unsupported for this operation. Only {extension_list} files are supported.").format( - {"extension": '".' + extension + '"', "extension_list": extension_list}) + "Only {extension_list} files are supported for this operation.").format( + {"extension_list": extension_list}) -static func get_file_dialog_select_mode_title_text(extensions: PackedStringArray) -> String: - if extensions.size() > 1: - return Translator.translate("Select an image") +static func get_file_dialog_select_mode_title_text(multi_select: bool, +extensions: PackedStringArray) -> String: + if multi_select: + return Translator.translate("Select {format} files").format( + {"format": get_extension_readable_name("svg")}) else: - # "an" because this can currently only show for SVG and XML files. - return Translator.translate("Select an {format} file").format( - {"format": get_extension_readable_name(extensions[0])}) + if extensions.size() > 1: + # Multiple formats currently only show up for reference images. + return Translator.translate("Select an image") + else: + # "an" because this can currently only show for SVG and XML files. + return Translator.translate("Select an {format} file").format( + {"format": get_extension_readable_name(extensions[0])}) static func get_file_dialog_save_mode_title_text(extension: String) -> String: return Translator.translate("Save the {format} file").format( diff --git a/src/utils/Utils.gd b/src/utils/Utils.gd index 196a73c..3dcc4ab 100644 --- a/src/utils/Utils.gd +++ b/src/utils/Utils.gd @@ -29,6 +29,19 @@ static func is_string_lower(string: String) -> bool: static func get_file_name(string: String) -> String: return string.get_file().trim_suffix("." + string.get_extension()) +# Method for showing the file path without stuff like "/home/mewpurpur/". +# This information is pretty much always unnecessary clutter. +static func simplify_file_path(file_path: String) -> String: + var home_dir: String + if OS.get_name() == "Windows": + home_dir = OS.get_environment("USERPROFILE") + else: + home_dir = OS.get_environment("HOME") + + if file_path.begins_with(home_dir): + return "~/" + file_path.trim_prefix(home_dir).trim_prefix("/").trim_prefix("\\") + return file_path + # Resize the control to be resized automatically to its text width, up to a maximum. # The property name defaults account for most controls that may need to use this. diff --git a/translations/GodSVG.pot b/translations/GodSVG.pot index 52226f4..020ce3a 100644 --- a/translations/GodSVG.pot +++ b/translations/GodSVG.pot @@ -65,7 +65,7 @@ msgid "" msgstr "" #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "" @@ -291,6 +291,10 @@ msgstr "" msgid "Height" msgstr "" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "" @@ -319,14 +323,14 @@ msgstr "" msgid "Search files" msgstr "" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "" @@ -768,7 +772,7 @@ msgstr "" msgid "This SVG is not bound to a file location yet." msgstr "" -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "" @@ -927,13 +931,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." msgstr "" #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "" +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "" @@ -955,7 +983,7 @@ msgid "Don't save" msgstr "" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +msgid "{file_path} is already being edited inside GodSVG." msgstr "" #: src/utils/FileUtils.gd @@ -986,6 +1014,14 @@ msgstr "" msgid "Close all other tabs" msgstr "" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "" @@ -1042,10 +1078,6 @@ msgstr "" msgid "Redo" msgstr "" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "" @@ -1244,23 +1276,21 @@ msgid "Viewport" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Select an image" +msgid "Select {format} files" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Select an {format} file" +msgid "Select an image" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Save the {format} file" +msgid "Select an {format} file" msgstr "" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." +msgid "Save the {format} file" msgstr "" #: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +msgid "Only {extension_list} files are supported for this operation." msgstr "" diff --git a/translations/bg.po b/translations/bg.po index 7fd6dbb..04bf35b 100644 --- a/translations/bg.po +++ b/translations/bg.po @@ -72,7 +72,7 @@ msgstr "" "определен." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Предупреждение!" @@ -300,6 +300,10 @@ msgstr "Широчина" msgid "Height" msgstr "Височина" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Копирай" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "Размерът на визуализацията е ограничен до {dimensions}" @@ -328,14 +332,14 @@ msgstr "Превключи видимостта на скритите файло msgid "Search files" msgstr "Търсене из файловете" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Избери" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Запиши" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Избери" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Пътека" @@ -794,7 +798,7 @@ msgstr "Превърти напред" msgid "This SVG is not bound to a file location yet." msgstr "Това SVG не е свързано с местоположение на компютъра." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Опитай отново" @@ -953,13 +957,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG не разпознава този атрибут" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." -msgstr "Файлът не можа да бъде отворен." +msgid "The following files were discarded:" +msgstr "Следните файлове бяха отхвърлени:" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "{file_path} бе отхвърлен." + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "Отхвърлени файлове" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "Продължи" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." +msgstr "{file_path} не можа да бъде отворен." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Проверете дали файлът все още съществува на избраното място." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "Продължи с импортирането на останалите файлове?" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "Продължи за всеки файл който не може да бъде отворен" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Записване на файла?" @@ -981,8 +1009,8 @@ msgid "Don't save" msgstr "Не записвай" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." -msgstr "Импортираният файл вече се редактира в GodSVG." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "{file_path} вече се редактира в GodSVG." #: src/utils/FileUtils.gd msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1014,6 +1042,14 @@ msgstr "Затвори разделите отдясно" msgid "Close all other tabs" msgstr "Затвори другите раздели" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "Затвори празните раздели" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "Затвори записаните раздели" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Създай раздел" @@ -1070,10 +1106,6 @@ msgstr "Върни назад" msgid "Redo" msgstr "Върни напред" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Копирай" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Постави" @@ -1271,6 +1303,10 @@ msgstr "Инспектор" msgid "Viewport" msgstr "Гледка" +#: src/utils/TranslationUtils.gd +msgid "Select {format} files" +msgstr "Избери {format} файлове" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Избери изображение" @@ -1284,13 +1320,5 @@ msgid "Save the {format} file" msgstr "Запази {format} файла" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"\"{extension}\" не е поддържан формат за тази операция. Единствено " -"{extension_list} файловете са подържани." - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." -msgstr "Форматът е празен. Единствено {extension_list} файловете са подържани." +msgid "Only {extension_list} files are supported for this operation." +msgstr "Единствено {extension_list} файлове се поддържат от тази операция." diff --git a/translations/de.po b/translations/de.po index a713733..3ccdeb9 100644 --- a/translations/de.po +++ b/translations/de.po @@ -72,7 +72,7 @@ msgstr "" "ist." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Achtung!" @@ -250,13 +250,12 @@ msgid "Snap size" msgstr "Rastergröße" #: src/ui_parts/display.gd -#, fuzzy msgid "Paste reference image" -msgstr "Referenzbild laden" +msgstr "Referenzbild einfügen" #: src/ui_parts/donate_menu.gd msgid "Links to donation platforms" -msgstr "" +msgstr "Links zu Spendenplattformen" #: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd #: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd @@ -304,6 +303,10 @@ msgstr "Breite" msgid "Height" msgstr "Höhe" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Kopieren" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "Vorschaubildgröße ist auf {dimensions} limitiert." @@ -332,14 +335,14 @@ msgstr "Versteckte Dateien anzeigen" msgid "Search files" msgstr "Datei suchen" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Auswählen" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Speichern" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Auswählen" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Pfad" @@ -799,7 +802,7 @@ msgstr "Nach vorne scrollen" msgid "This SVG is not bound to a file location yet." msgstr "Dieses SVG ist noch nicht an einen Dateiort gebunden." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Erneut versuchen" @@ -821,18 +824,16 @@ msgid "Update check failed" msgstr "Aktualisierungsprüfung fehlgeschlagen" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "View all releases" -msgstr "Alle Elemente auswählen" +msgstr "Alle Veröffentlichungen anzeigen" #: src/ui_parts/update_menu.gd msgid "GodSVG is up-to-date." msgstr "GodSVG ist auf dem neuesten Stand." #: src/ui_parts/update_menu.gd -#, fuzzy msgid "New versions available!" -msgstr "Neue Versionen" +msgstr "Neue Versionen verfügbar!" #: src/ui_widgets/choose_name_dialog.gd msgid "Create" @@ -960,13 +961,38 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG erkennt diese Attribute nicht" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} couldn't be opened." msgstr "Die Datei konnte nicht geöffnet werden." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Prüfen, ob die Datei im ausgewählten Dateipfad noch existiert." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Datei speichern?" @@ -988,7 +1014,8 @@ msgid "Don't save" msgstr "Nicht speichern" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +#, fuzzy +msgid "{file_path} is already being edited inside GodSVG." msgstr "Die importierte Datei wird bereits in GodSVG bearbeitet." #: src/utils/FileUtils.gd @@ -1021,6 +1048,16 @@ msgstr "Alle Registerkarten rechts schließen" msgid "Close all other tabs" msgstr "Alle anderen Registerkarten schließen" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close empty tabs" +msgstr "Registerkarte schließen" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close saved tabs" +msgstr "Alle anderen Registerkarten schließen" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Registerkarte erstellen" @@ -1077,10 +1114,6 @@ msgstr "Rückgängig" msgid "Redo" msgstr "Wiederholen" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Kopieren" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Einfügen" @@ -1278,6 +1311,11 @@ msgstr "Inspektor" msgid "Viewport" msgstr "Ansichtsfenster" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Select {format} files" +msgstr "{format}-Datei auswählen" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Bild auswählen" @@ -1291,19 +1329,19 @@ msgid "Save the {format} file" msgstr "Die {format}-Datei speichern" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"Die Dateierweiterung \"{extension}\" ist für diese Operation nicht " -"unterstützt. Nur {extension_list} Dateien werden unterstützt." - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." msgstr "" "Die Dateierweiterung ist leer. Nur {extension_list} Dateien werden " "unterstützt." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "Die Dateierweiterung \"{extension}\" ist für diese Operation nicht " +#~ "unterstützt. Nur {extension_list} Dateien werden unterstützt." + #~ msgid "Save the .\"{format}\" file" #~ msgstr "Die .\"{format}\" Datei speichern" diff --git a/translations/en.po b/translations/en.po index 9e571f6..d219e99 100644 --- a/translations/en.po +++ b/translations/en.po @@ -66,7 +66,7 @@ msgid "" msgstr "" #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "" @@ -292,6 +292,10 @@ msgstr "" msgid "Height" msgstr "" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "" @@ -320,14 +324,14 @@ msgstr "" msgid "Search files" msgstr "" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "" @@ -769,7 +773,7 @@ msgstr "" msgid "This SVG is not bound to a file location yet." msgstr "" -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "" @@ -928,13 +932,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." msgstr "" #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "" +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "" @@ -956,7 +984,7 @@ msgid "Don't save" msgstr "" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +msgid "{file_path} is already being edited inside GodSVG." msgstr "" #: src/utils/FileUtils.gd @@ -987,6 +1015,14 @@ msgstr "" msgid "Close all other tabs" msgstr "" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "" @@ -1043,10 +1079,6 @@ msgstr "" msgid "Redo" msgstr "" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "" @@ -1245,23 +1277,21 @@ msgid "Viewport" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Select an image" +msgid "Select {format} files" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Select an {format} file" +msgid "Select an image" msgstr "" #: src/utils/TranslationUtils.gd -msgid "Save the {format} file" +msgid "Select an {format} file" msgstr "" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." +msgid "Save the {format} file" msgstr "" #: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +msgid "Only {extension_list} files are supported for this operation." msgstr "" diff --git a/translations/es.po b/translations/es.po new file mode 100644 index 0000000..d23b74d --- /dev/null +++ b/translations/es.po @@ -0,0 +1,1340 @@ +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: GodSVG\n" +"POT-Creation-Date: \n" +"PO-Revision-Date: \n" +"Last-Translator: \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.6\n" + +msgid "translation-credits" +msgstr "Alejandro Moctezuma " + +#: src/autoload/HandlerGUI.gd +msgid "Quit GodSVG" +msgstr "Salir de GodSVG" + +#: src/autoload/HandlerGUI.gd +msgid "Do you want to quit GodSVG?" +msgstr "¿Quieres salir de GodSVG?" + +#: src/autoload/HandlerGUI.gd +msgid "Quit" +msgstr "Salir" + +#: src/autoload/HandlerGUI.gd +msgid "Check for updates?" +msgstr "¿Buscar actualizaciones?" + +#: src/autoload/HandlerGUI.gd +msgid "" +"This will connect to github.com to compare version numbers. No other data is " +"collected or transmitted." +msgstr "" +"Esto se conectará a github.com para comparar los números de versión. No se " +"recopilan ni transmiten otros datos." + +#: src/autoload/HandlerGUI.gd src/ui_widgets/alert_dialog.gd +msgid "OK" +msgstr "OK" + +#: src/autoload/HandlerGUI.gd +msgid "Do you want to proceed?" +msgstr "¿Quieres continuar?" + +#: src/autoload/HandlerGUI.gd +msgid "Export SVG" +msgstr "Exportar SVG" + +#: src/autoload/HandlerGUI.gd src/ui_parts/export_menu.gd +#: src/utils/TranslationUtils.gd +msgid "Export" +msgstr "Exportar" + +#: src/autoload/HandlerGUI.gd +msgid "" +"The graphic can be exported only as SVG because its proportions are too " +"extreme." +msgstr "" +"El gráfico solo se puede exportar como SVG porque sus proporciones son " +"demasiado extremas." + +#: src/autoload/HandlerGUI.gd +msgid "" +"The graphic can be exported only as SVG because its size is not defined." +msgstr "" +"El gráfico solo se puede exportar como SVG porque su tamaño no está definido." + +#: src/autoload/State.gd src/ui_parts/good_file_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd +msgid "Alert!" +msgstr "¡Advertencia!" + +#: src/autoload/State.gd src/utils/TranslationUtils.gd +msgid "Close tab" +msgstr "Cerrar pestaña" + +#: src/autoload/State.gd +msgid "Restore" +msgstr "Restaurar" + +#: src/autoload/State.gd +msgid "View in Inspector" +msgstr "Ver en el Inspector" + +#: src/autoload/State.gd +msgid "Convert To" +msgstr "Convertir a" + +#: src/autoload/State.gd src/ui_widgets/transform_popup.gd +msgid "Insert After" +msgstr "Insertar después" + +#: src/autoload/State.gd +msgid "The last edited state of this tab could not be found." +msgstr "No se pudo encontrar el último estado editado de esta pestaña." + +#: src/autoload/State.gd +msgid "" +"The tab is bound to the file path {file_path}. Do you want to restore the " +"SVG from this path?" +msgstr "" +"La pestaña está vinculada a la ruta del archivo {file_path}. ¿Quieres " +"restaurar el SVG desde esta ruta?" + +#: src/config_classes/Formatter.gd +msgid "Compact" +msgstr "Compacto" + +#: src/config_classes/Formatter.gd +msgid "Pretty" +msgstr "Chulo" + +#: src/config_classes/Formatter.gd +msgid "Always" +msgstr "Siempre" + +#: src/config_classes/Formatter.gd +msgid "All except containers" +msgstr "Todo excepto contenedores" + +#: src/config_classes/Formatter.gd +msgid "Never" +msgstr "Nunca" + +#: src/config_classes/Formatter.gd +msgid "When shorter or equal" +msgstr "Cuando es más corto o igual que" + +#: src/config_classes/Formatter.gd +msgid "When shorter" +msgstr "Cuando es más corto que" + +#: src/config_classes/Formatter.gd +msgid "3-digit or 6-digit hex" +msgstr "Hexadecimal de 3 o 6 dígitos" + +#: src/config_classes/Formatter.gd +msgid "6-digit hex" +msgstr "Hexadecimal de 6 dígitos" + +#: src/config_classes/TabData.gd +msgid "Empty" +msgstr "Vacío" + +#: src/config_classes/TabData.gd +msgid "Unsaved" +msgstr "Sin guardar" + +#: src/data_classes/BasicXNode.gd +msgid "Comment" +msgstr "Comentario" + +#: src/data_classes/BasicXNode.gd +msgid "Text" +msgstr "Texto" + +#: src/data_classes/Element.gd +msgid "{element} must be inside {allowed} to have any effect." +msgstr "{element} debe estar dentro de {allowed} para tener algún efecto." + +#: src/data_classes/ElementG.gd +msgid "This group has no elements." +msgstr "Este grupo no tiene elementos." + +#: src/data_classes/ElementG.gd +msgid "This group has only one element." +msgstr "Este grupo solo tiene un elemento." + +#: src/data_classes/GradientUtils.gd +msgid "No \"id\" attribute defined." +msgstr "No hay ningún atributo \"id\" definido." + +#: src/data_classes/GradientUtils.gd +msgid "No elements under this gradient." +msgstr "No hay elementos en este gradiente." + +#: src/data_classes/GradientUtils.gd +msgid "This gradient is a solid color." +msgstr "Este gradiente es un color sólido." + +#: src/data_classes/SVGParser.gd +msgid "Doesn’t describe an SVG." +msgstr "No describe un SVG." + +#: src/data_classes/SVGParser.gd +msgid "Improper nesting." +msgstr "Anidación inadecuada." + +#: src/ui_parts/about_menu.gd src/ui_parts/good_file_dialog.gd +#: src/ui_parts/settings_menu.gd src/ui_parts/shortcut_panel_config.gd +#: src/ui_parts/update_menu.gd +msgid "Close" +msgstr "Cerrar" + +#: src/ui_parts/about_menu.gd +msgid "Authors" +msgstr "Autores" + +#: src/ui_parts/about_menu.gd +msgid "Donors" +msgstr "Donadores" + +#: src/ui_parts/about_menu.gd +msgid "License" +msgstr "Licencia" + +#: src/ui_parts/about_menu.gd +msgid "Third-party licenses" +msgstr "Licencias de terceros" + +#: src/ui_parts/about_menu.gd +msgid "Project Founder and Manager" +msgstr "Fundador y administrador del proyecto" + +#: src/ui_parts/about_menu.gd +msgid "Developers" +msgstr "Desarrolladores" + +#: src/ui_parts/about_menu.gd +msgid "Translators" +msgstr "Traductores" + +#: src/ui_parts/about_menu.gd +msgid "Golden donors" +msgstr "Donadores dorados" + +#: src/ui_parts/about_menu.gd +msgid "Diamond donors" +msgstr "Donadores de diamante" + +#: src/ui_parts/current_file_button.gd +msgid "Save SVG as…" +msgstr "Guardar SVG como…" + +#: src/ui_parts/display.gd +msgid "Visuals" +msgstr "Visuales" + +#: src/ui_parts/display.gd +msgid "Snap size" +msgstr "Tamaño de ajuste" + +#: src/ui_parts/display.gd +msgid "Paste reference image" +msgstr "Pegar imagen de referencia" + +#: src/ui_parts/donate_menu.gd +msgid "Links to donation platforms" +msgstr "Enlaces de las plataformas de donación" + +#: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd +#: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd +#: src/ui_widgets/confirm_dialog.gd src/ui_widgets/options_dialog.gd +msgid "Cancel" +msgstr "Cancelar" + +#: src/ui_parts/element_container.gd +msgid "New element" +msgstr "Nuevo elemento" + +#: src/ui_parts/export_menu.gd +msgid "Dimensions" +msgstr "Dimensiones" + +#: src/ui_parts/export_menu.gd +msgid "Size" +msgstr "Tamaño" + +#: src/ui_parts/export_menu.gd +msgid "Export Configuration" +msgstr "Exportar configuración" + +#: src/ui_parts/export_menu.gd +msgid "Format" +msgstr "Formato" + +#: src/ui_parts/export_menu.gd +msgid "Lossless" +msgstr "Sin pérdida" + +#: src/ui_parts/export_menu.gd +msgid "Quality" +msgstr "Calidad" + +#: src/ui_parts/export_menu.gd +msgid "Scale" +msgstr "Escala" + +#: src/ui_parts/export_menu.gd +msgid "Width" +msgstr "Ancho" + +#: src/ui_parts/export_menu.gd +msgid "Height" +msgstr "Alto" + +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Copiar" + +#: src/ui_parts/export_menu.gd +msgid "Preview image size is limited to {dimensions}" +msgstr "El tamaño de la imagen de vista previa está limitado a {dimensions}" + +#: src/ui_parts/global_actions.gd src/ui_parts/shortcut_panel_config.gd +msgid "Layout" +msgstr "Diseño" + +#: src/ui_parts/global_actions.gd +msgid "View savedata" +msgstr "Ver datos guardados" + +#: src/ui_parts/good_file_dialog.gd +msgid "Go to parent folder" +msgstr "Ir a la carpeta contenedora" + +#: src/ui_parts/good_file_dialog.gd +msgid "Refresh files" +msgstr "Actualizar archivos" + +#: src/ui_parts/good_file_dialog.gd +msgid "Toggle the visibility of hidden files" +msgstr "Alternar visibilidad de archivos ocultos" + +#: src/ui_parts/good_file_dialog.gd +msgid "Search files" +msgstr "Buscar archivos" + +#: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd +msgid "Save" +msgstr "Guardar" + +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Seleccionar" + +#: src/ui_parts/good_file_dialog.gd +msgid "Path" +msgstr "Ruta" + +#: src/ui_parts/good_file_dialog.gd +msgid "Replace" +msgstr "Reemplazar" + +#: src/ui_parts/good_file_dialog.gd +msgid "Create new folder" +msgstr "Crear nueva carpeta" + +#: src/ui_parts/good_file_dialog.gd +msgid "Invalid name for a folder." +msgstr "Nombre inválido para una carpeta." + +#: src/ui_parts/good_file_dialog.gd +msgid "A folder with this name already exists." +msgstr "Ya existe una carpeta con este nombre." + +#: src/ui_parts/good_file_dialog.gd +msgid "Failed to create a folder." +msgstr "Error al crear una carpeta." + +#: src/ui_parts/good_file_dialog.gd +msgid "Open" +msgstr "Abrir" + +#: src/ui_parts/good_file_dialog.gd +msgid "Copy path" +msgstr "Copiar ruta" + +#: src/ui_parts/good_file_dialog.gd +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "" +"Ya existe un archivo llamado \"{file_name}\". ¡Reemplazarlo sobrescribirá su " +"contenido!" + +#: src/ui_parts/handles_manager.gd +msgid "New shape" +msgstr "Nueva forma" + +#: src/ui_parts/import_warning_menu.gd +msgid "Import Problems" +msgstr "Problemas de la importación" + +#: src/ui_parts/import_warning_menu.gd src/utils/TranslationUtils.gd +msgid "Import" +msgstr "Importar" + +#: src/ui_parts/import_warning_menu.gd +msgid "Unrecognized element" +msgstr "Elemento no reconocido" + +#: src/ui_parts/import_warning_menu.gd +msgid "Unrecognized attribute" +msgstr "Atributo no reconocido" + +#: src/ui_parts/import_warning_menu.gd +msgid "Syntax error" +msgstr "Error de sintaxis" + +#: src/ui_parts/inspector.gd +msgid "Add element" +msgstr "Añadir elemento" + +#. Refers to the zero, one, or multiple UI parts to not be shown in the final layout. It's of plural cardinality. +#: src/ui_parts/layout_popup.gd +msgid "Excluded" +msgstr "Excluidos" + +#: src/ui_parts/layout_popup.gd +msgid "Drag and drop to change the layout" +msgstr "Arrastra y suelta para cambiar el diseño" + +#: src/ui_parts/mac_menu.gd src/ui_parts/settings_menu.gd +msgid "File" +msgstr "Archivo" + +#: src/ui_parts/mac_menu.gd src/ui_parts/settings_menu.gd +msgid "Edit" +msgstr "Editar" + +#: src/ui_parts/mac_menu.gd src/ui_parts/settings_menu.gd +msgid "Tool" +msgstr "Herramientas" + +#: src/ui_parts/mac_menu.gd src/ui_parts/settings_menu.gd +msgid "View" +msgstr "Ver" + +#: src/ui_parts/mac_menu.gd +msgid "Snap" +msgstr "Ajuste" + +#: src/ui_parts/settings_menu.gd +msgid "Formatting" +msgstr "Formato" + +#: src/ui_parts/settings_menu.gd src/ui_widgets/color_field_popup.gd +msgid "Palettes" +msgstr "Paletas" + +#: src/ui_parts/settings_menu.gd +msgid "Shortcuts" +msgstr "Atajos" + +#: src/ui_parts/settings_menu.gd +msgid "Theming" +msgstr "Temas" + +#: src/ui_parts/settings_menu.gd +msgid "Tab bar" +msgstr "Barra de pestañas" + +#: src/ui_parts/settings_menu.gd +msgid "Other" +msgstr "Otro" + +#: src/ui_parts/settings_menu.gd +msgid "SVG Text colors" +msgstr "Colores del texto de los SVG" + +#: src/ui_parts/settings_menu.gd +msgid "Symbol color" +msgstr "Color de los símbolos" + +#: src/ui_parts/settings_menu.gd +msgid "Element color" +msgstr "Color de los elementos" + +#: src/ui_parts/settings_menu.gd +msgid "Attribute color" +msgstr "Color de los atributos" + +#: src/ui_parts/settings_menu.gd +msgid "String color" +msgstr "Color de las cadenas" + +#: src/ui_parts/settings_menu.gd +msgid "Comment color" +msgstr "Color de los comentarios" + +#: src/ui_parts/settings_menu.gd +msgid "Text color" +msgstr "Color del texto" + +#. CDATA shouldn't be translated. It's a type of XML section. +#: src/ui_parts/settings_menu.gd +msgid "CDATA color" +msgstr "Color de CDATA" + +#: src/ui_parts/settings_menu.gd +msgid "Error color" +msgstr "Color de los errores" + +#. Refers to the colors of the draggable handles. +#: src/ui_parts/settings_menu.gd +msgid "Handle colors" +msgstr "Colores de los controladores" + +#: src/ui_parts/settings_menu.gd +msgid "Inside color" +msgstr "Color interno" + +#: src/ui_parts/settings_menu.gd +msgid "Normal color" +msgstr "Color normal" + +#: src/ui_parts/settings_menu.gd +msgid "Hovered color" +msgstr "Color flotante" + +#: src/ui_parts/settings_menu.gd +msgid "Selected color" +msgstr "Color seleccionado" + +#: src/ui_parts/settings_menu.gd +msgid "Hovered selected color" +msgstr "Color flotante seleccionado" + +#: src/ui_parts/settings_menu.gd +msgid "Basic colors" +msgstr "Colores básicos" + +#: src/ui_parts/settings_menu.gd +msgid "Background color" +msgstr "Color del fondo" + +#: src/ui_parts/settings_menu.gd +msgid "Grid color" +msgstr "Color de la cuadrícula" + +#: src/ui_parts/settings_menu.gd +msgid "Valid color" +msgstr "Color válido" + +#: src/ui_parts/settings_menu.gd +msgid "Warning color" +msgstr "Color de las advertencias" + +#: src/ui_parts/settings_menu.gd +msgid "Input" +msgstr "Entrada" + +#: src/ui_parts/settings_menu.gd +msgid "Close tabs with middle mouse button" +msgstr "Cerrar pestañas con el botón central del ratón" + +#: src/ui_parts/settings_menu.gd +msgid "Invert zoom direction" +msgstr "Invertir dirección de zoom" + +#: src/ui_parts/settings_menu.gd +msgid "Wrap-around panning" +msgstr "Desplazamiento envolvente" + +#: src/ui_parts/settings_menu.gd +msgid "Use CTRL for zooming" +msgstr "Usar Ctrl para hacer zoom" + +#: src/ui_parts/settings_menu.gd +msgid "Miscellaneous" +msgstr "Misceláneos" + +#: src/ui_parts/settings_menu.gd +msgid "Use native file dialog" +msgstr "Usar diálogo de archivos nativo" + +#: src/ui_parts/settings_menu.gd +msgid "Sync window title to file name" +msgstr "Sincronizar título de la ventana con el nombre del archivo" + +#. Refers to the size of the draggable handles. +#: src/ui_parts/settings_menu.gd +msgid "Handle size" +msgstr "Tamaño de los controladores" + +#: src/ui_parts/settings_menu.gd +msgid "UI scale" +msgstr "Escala de la interfaz de usuario" + +#: src/ui_parts/settings_menu.gd +msgid "Changes the scale factor for the interface." +msgstr "Cambia el factor de escala de la interfaz." + +#: src/ui_parts/settings_menu.gd +msgid "Language" +msgstr "Idioma" + +#: src/ui_parts/settings_menu.gd +msgid "Import XML" +msgstr "Importar XML" + +#: src/ui_parts/settings_menu.gd +msgid "Paste XML" +msgstr "Pegar XML" + +#: src/ui_parts/settings_menu.gd +msgid "New palette" +msgstr "Nueva paleta" + +#: src/ui_parts/settings_menu.gd +msgid "New palette from XML" +msgstr "Nueva paleta desde XML" + +#. Refers to the formatter used for GodSVG's code editor. +#: src/ui_parts/settings_menu.gd +msgid "Editor formatter" +msgstr "Formato del editor" + +#. Refers to the formatter used when exporting. +#: src/ui_parts/settings_menu.gd +msgid "Export formatter" +msgstr "Formato de exportación" + +#: src/ui_parts/settings_menu.gd +msgid "Help" +msgstr "Ayuda" + +#: src/ui_parts/settings_menu.gd +msgid "Reset all to default" +msgstr "Restablecer todo a los valores predeterminados" + +#: src/ui_parts/settings_menu.gd +msgid "Preset" +msgstr "Preajuste" + +#: src/ui_parts/settings_menu.gd +msgid "Keep comments" +msgstr "Mantener comentarios" + +#: src/ui_parts/settings_menu.gd +msgid "Keep unrecognized XML structures" +msgstr "Mantener estructuras XML no reconocidas" + +#: src/ui_parts/settings_menu.gd +msgid "Add trailing newline" +msgstr "Agregar nueva línea final" + +#: src/ui_parts/settings_menu.gd +msgid "Use shorthand tag syntax" +msgstr "Usar la sintaxis de etiqueta abreviada" + +#: src/ui_parts/settings_menu.gd +msgid "Space out the slash of shorthand tags" +msgstr "Espaciar la barra diagonal de las etiquetas abreviadas" + +#: src/ui_parts/settings_menu.gd +msgid "Use pretty formatting" +msgstr "Usar formato chulo" + +#: src/ui_parts/settings_menu.gd +msgid "Use spaces instead of tabs" +msgstr "Usar espacios en vez de pestañas" + +#: src/ui_parts/settings_menu.gd +msgid "Number of indentation spaces" +msgstr "Número de espacios de sangría" + +#: src/ui_parts/settings_menu.gd +msgid "Numbers" +msgstr "Números" + +#: src/ui_parts/settings_menu.gd +msgid "Remove leading zero" +msgstr "Eliminar ceros iniciales" + +#: src/ui_parts/settings_menu.gd +msgid "Use exponential when shorter" +msgstr "Usar exponenciales cuando sean más cortos" + +#: src/ui_parts/settings_menu.gd +msgid "Colors" +msgstr "Colores" + +#: src/ui_parts/settings_menu.gd +msgid "Use named colors" +msgstr "Usar colores con nombre" + +#: src/ui_parts/settings_menu.gd +msgid "Primary syntax" +msgstr "Sintaxis primaria" + +#: src/ui_parts/settings_menu.gd +msgid "Capitalize hexadecimal letters" +msgstr "Poner en mayúsculas las letras hexadecimales" + +#: src/ui_parts/settings_menu.gd +msgid "Pathdata" +msgstr "Datos de ruta" + +#: src/ui_parts/settings_menu.gd +msgid "Compress numbers" +msgstr "Comprimir números" + +#: src/ui_parts/settings_menu.gd +msgid "Minimize spacing" +msgstr "Minimizar el espaciado" + +#: src/ui_parts/settings_menu.gd +msgid "Remove spacing after flags" +msgstr "Eliminar espaciado después de banderas" + +#: src/ui_parts/settings_menu.gd +msgid "Remove consecutive commands" +msgstr "Eliminar comandos consecutivos" + +#: src/ui_parts/settings_menu.gd +msgid "Transform lists" +msgstr "Transformar listas" + +#: src/ui_parts/settings_menu.gd +msgid "Remove unnecessary parameters" +msgstr "Eliminar parámetros innecesarios" + +#: src/ui_parts/settings_menu.gd +msgid "" +"If turned on, clicking on a tab with the middle mouse button closes the tab. " +"If turned off, it focuses the tab instead." +msgstr "" +"Si está activado, al hacer clic en una pestaña con el botón central del " +"ratón, esta se cierra. Si está desactivado, se centra en la pestaña." + +#: src/ui_parts/settings_menu.gd +msgid "Swaps the scroll directions for zooming in and zooming out." +msgstr "Intercambia las direcciones de desplazamiento para acercar y alejar." + +#: src/ui_parts/settings_menu.gd +msgid "" +"Warps the cursor to the opposite side whenever it reaches a viewport " +"boundary while panning." +msgstr "" +"Mueve el cursor hacia el lado opuesto cada vez que alcanza el límite de la " +"ventana gráfica mientras se desplaza." + +#: src/ui_parts/settings_menu.gd +msgid "" +"If turned on, scrolling pans the view. To zoom, hold CTRL while scrolling." +msgstr "" +"Si está activado, el desplazamiento panorámico permite ver la vista " +"panorámica. Para hacer zoom, mantén presionada la tecla Ctrl mientras te " +"desplazas." + +#: src/ui_parts/settings_menu.gd +msgid "" +"If turned on, uses your operating system's native file dialog. If turned " +"off, uses GodSVG's built-in file dialog." +msgstr "" +"Si está activado, usa el cuadro de diálogo de archivos nativo de tu sistema " +"operativo. Si está desactivado, usa el cuadro de diálogo de archivos " +"integrado de GodSVG." + +#: src/ui_parts/settings_menu.gd +msgid "" +"If turned off, the window title remains as \"GodSVG\" without including the " +"current file." +msgstr "" +"Si está desactivado, el título de la ventana permanece como \"GodSVG\" sin " +"incluir el archivo actual." + +#: src/ui_parts/settings_menu.gd +msgid "Changes the visual size and grabbing area of handles." +msgstr "Cambia el tamaño visual y el área de agarre de los controladores." + +#: src/ui_parts/shortcut_panel.gd +msgid "Horizontal strip" +msgstr "Franja horizontal" + +#: src/ui_parts/shortcut_panel.gd +msgid "Vertical strip" +msgstr "Franja vertical" + +#: src/ui_parts/shortcut_panel.gd +msgid "Horizontal with two rows" +msgstr "Horizontal con dos filas" + +#: src/ui_parts/shortcut_panel_config.gd +msgid "Configure Shortcut Panel" +msgstr "Configurar el panel de accesos directos" + +#: src/ui_parts/tab_bar.gd src/utils/TranslationUtils.gd +msgid "Create a new tab" +msgstr "Crear una nueva pestaña" + +#: src/ui_parts/tab_bar.gd +msgid "Scroll backwards" +msgstr "Desplazarse hacia atrás" + +#: src/ui_parts/tab_bar.gd +msgid "Scroll forwards" +msgstr "Desplazarse hacia adelante" + +#: src/ui_parts/tab_bar.gd +msgid "This SVG is not bound to a file location yet." +msgstr "Este SVG aún no está vinculado a una ubicación de archivo." + +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd +msgid "Retry" +msgstr "Reintentar" + +#: src/ui_parts/update_menu.gd +msgid "Show prereleases" +msgstr "Mostrar prelanzamientos" + +#: src/ui_parts/update_menu.gd +msgid "Current Version" +msgstr "Versión actual" + +#: src/ui_parts/update_menu.gd +msgid "Retrieving information..." +msgstr "Recuperando la información..." + +#. When checking for updates. +#: src/ui_parts/update_menu.gd +msgid "Update check failed" +msgstr "Error en la verificación de actualizaciones" + +#: src/ui_parts/update_menu.gd +msgid "View all releases" +msgstr "Ver todos los lanzamientos" + +#: src/ui_parts/update_menu.gd +msgid "GodSVG is up-to-date." +msgstr "GodSVG está actualizado." + +#: src/ui_parts/update_menu.gd +msgid "New versions available!" +msgstr "¡Nuevas versiones disponibles!" + +#: src/ui_widgets/choose_name_dialog.gd +msgid "Create" +msgstr "Crear" + +#: src/ui_widgets/color_field_popup.gd +msgid "Search color" +msgstr "Buscar color" + +#: src/ui_widgets/color_field_popup.gd +msgid "Color Picker" +msgstr "Cuentagotas" + +#: src/ui_widgets/configure_color_popup.gd +msgid "Edit color name" +msgstr "Editar nombre del color" + +#: src/ui_widgets/configure_color_popup.gd +msgid "Delete color" +msgstr "Eliminar color" + +#: src/ui_widgets/configure_color_popup.gd src/ui_widgets/palette_config.gd +msgid "Unnamed" +msgstr "Sin nombre" + +#: src/ui_widgets/good_color_picker.gd +msgid "Color keywords" +msgstr "Palabras clave de colores" + +#: src/ui_widgets/good_color_picker.gd +msgid "Eyedropper" +msgstr "Cuentagotas" + +#: src/ui_widgets/palette_config.gd +msgid "Unnamed palettes won't be shown." +msgstr "No se mostrarán las paletas sin nombre." + +#: src/ui_widgets/palette_config.gd +msgid "Multiple palettes can't have the same name." +msgstr "Varias paletas no pueden tener el mismo nombre." + +#: src/ui_widgets/palette_config.gd +msgid "This palette has identically defined colors." +msgstr "Esta paleta tiene colores definidos idénticamente." + +#: src/ui_widgets/palette_config.gd +msgid "Rename" +msgstr "Renombrar" + +#: src/ui_widgets/palette_config.gd +msgid "Move Up" +msgstr "Mover hacia arriba" + +#: src/ui_widgets/palette_config.gd +msgid "Move Down" +msgstr "Mover hacia abajo" + +#: src/ui_widgets/palette_config.gd +msgid "Apply Preset" +msgstr "Aplicar preajuste" + +#: src/ui_widgets/palette_config.gd src/ui_widgets/setting_shortcut.gd +#: src/ui_widgets/transform_popup.gd src/utils/TranslationUtils.gd +msgid "Delete" +msgstr "Eliminar" + +#: src/ui_widgets/palette_config.gd +msgid "Copy as XML" +msgstr "Copiar como XML" + +#: src/ui_widgets/palette_config.gd +msgid "Save as XML" +msgstr "Guardar como XML" + +#: src/ui_widgets/path_popup.gd src/utils/TranslationUtils.gd +msgid "Relative" +msgstr "Relativo" + +#: src/ui_widgets/pathdata_field.gd +msgid "No path data" +msgstr "Sin datos de ruta" + +#: src/ui_widgets/points_field.gd +msgid "No points" +msgstr "Sin puntos" + +#: src/ui_widgets/presented_shortcut.gd src/ui_widgets/setting_shortcut.gd +msgid "Also used by" +msgstr "También usado por" + +#: src/ui_widgets/setting_frame.gd src/ui_widgets/setting_shortcut.gd +msgid "Reset to default" +msgstr "Reestablecer a los valores predeterminados" + +#: src/ui_widgets/setting_shortcut.gd +msgid "Unused" +msgstr "No usado" + +#: src/ui_widgets/setting_shortcut.gd +msgid "Add shortcut" +msgstr "Añadir atajo" + +#: src/ui_widgets/setting_shortcut.gd +msgid "Press keys…" +msgstr "Presiona una tecla…" + +#: src/ui_widgets/transform_field.gd +msgid "No transforms" +msgstr "Sin transformaciones" + +#: src/ui_widgets/transform_popup.gd +msgid "Apply the matrix" +msgstr "Aplicar la matriz" + +#: src/ui_widgets/transform_popup.gd +msgid "Insert Before" +msgstr "Insertar antes" + +#: src/ui_widgets/transform_popup.gd +msgid "New transform" +msgstr "Nueva transformación" + +#: src/ui_widgets/unrecognized_field.gd +msgid "GodSVG doesn’t recognize this attribute" +msgstr "GodSVG no reconoce este atributo" + +#: src/utils/FileUtils.gd +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} couldn't be opened." +msgstr "No se pudo abrir el archivo." + +#: src/utils/FileUtils.gd +msgid "Check if the file still exists in the selected file path." +msgstr "" +"Compruebe si el archivo todavía existe en la ruta de archivo seleccionada." + +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Save the file?" +msgstr "¿Guardar el archivo?" + +#: src/utils/FileUtils.gd +msgid "Do you want to save this file?" +msgstr "¿Quieres guardar este archivo?" + +#: src/utils/FileUtils.gd +msgid "Save the changes?" +msgstr "¿Guardar los cambios?" + +#: src/utils/FileUtils.gd +msgid "Your changes will be lost if you don't save them." +msgstr "Tus cambios se perderán si no los guardas." + +#: src/utils/FileUtils.gd +msgid "Don't save" +msgstr "No guardar" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} is already being edited inside GodSVG." +msgstr "El archivo importado ya se está editando dentro de GodSVG." + +#: src/utils/FileUtils.gd +msgid "If you want to revert your edits since the last save, use {reset_svg}." +msgstr "" +"Si deseas revertir las ediciones realizadas desde la última vez que se " +"guardó, utiliza {reset_svg}." + +#: src/utils/FileUtils.gd +msgid "Do you want to save the changes made to {file_name}?" +msgstr "¿Quieres guardar los cambios realizados en {file_name}?" + +#: src/utils/TranslationUtils.gd +msgid "Save SVG" +msgstr "Guardar SVG" + +#: src/utils/TranslationUtils.gd +msgid "Save SVG as" +msgstr "Guardar SVG como" + +#: src/utils/TranslationUtils.gd +msgid "Close tabs to the left" +msgstr "Cerrar pestañas a la izquierda" + +#: src/utils/TranslationUtils.gd +msgid "Close tabs to the right" +msgstr "Cerrar pestañas a la derecha" + +#: src/utils/TranslationUtils.gd +msgid "Close all other tabs" +msgstr "Cerrar todas las demás pestañas" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close empty tabs" +msgstr "Cerrar pestaña" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close saved tabs" +msgstr "Cerrar todas las demás pestañas" + +#: src/utils/TranslationUtils.gd +msgid "Create tab" +msgstr "Crear pestaña" + +#: src/utils/TranslationUtils.gd +msgid "Select the next tab" +msgstr "Seleccionar la siguiente pestaña" + +#: src/utils/TranslationUtils.gd +msgid "Select the previous tab" +msgstr "Seleccionar la pestaña anterior" + +#: src/utils/TranslationUtils.gd +msgid "Optimize" +msgstr "Optimizar" + +#: src/utils/TranslationUtils.gd +msgid "Optimize SVG" +msgstr "Optimizar SVG" + +#: src/utils/TranslationUtils.gd +msgid "Copy all text" +msgstr "Copiar todo el texto" + +#: src/utils/TranslationUtils.gd +msgid "Copy the SVG text" +msgstr "Copiar todo el texto SVG" + +#: src/utils/TranslationUtils.gd +msgid "Reset SVG" +msgstr "Reestablecer SVG" + +#: src/utils/TranslationUtils.gd +msgid "Open externally" +msgstr "Abrir externamente" + +#: src/utils/TranslationUtils.gd +msgid "Open SVG externally" +msgstr "Abrir SVG externamente" + +#: src/utils/TranslationUtils.gd +msgid "Show in File Manager" +msgstr "Mostrar en Explorador de Archivos" + +#: src/utils/TranslationUtils.gd +msgid "Show SVG in File Manager" +msgstr "Mostrar SVG en Explorador de Archivos" + +#: src/utils/TranslationUtils.gd +msgid "Undo" +msgstr "Deshacer" + +#: src/utils/TranslationUtils.gd +msgid "Redo" +msgstr "Rehacer" + +#: src/utils/TranslationUtils.gd +msgid "Paste" +msgstr "Pegar" + +#: src/utils/TranslationUtils.gd +msgid "Cut" +msgstr "Cortar" + +#: src/utils/TranslationUtils.gd +msgid "Select all" +msgstr "Seleccionar todo" + +#: src/utils/TranslationUtils.gd +msgid "Duplicate" +msgstr "Duplicar" + +#: src/utils/TranslationUtils.gd +msgid "Duplicate the selection" +msgstr "Duplicar la selección" + +#: src/utils/TranslationUtils.gd +msgid "Delete the selection" +msgstr "Eliminar la selección" + +#: src/utils/TranslationUtils.gd +msgid "Move up" +msgstr "Mover hacia arriba" + +#: src/utils/TranslationUtils.gd +msgid "Move the selection up" +msgstr "Mover la selección hacia arriba" + +#: src/utils/TranslationUtils.gd +msgid "Move down" +msgstr "Mover hacia abajo" + +#: src/utils/TranslationUtils.gd +msgid "Move the selection down" +msgstr "Mover la selección hacia abajo" + +#: src/utils/TranslationUtils.gd +msgid "Find" +msgstr "Buscar" + +#: src/utils/TranslationUtils.gd +msgid "Zoom in" +msgstr "Acercarse" + +#: src/utils/TranslationUtils.gd +msgid "Zoom out" +msgstr "Alejarse" + +#: src/utils/TranslationUtils.gd +msgid "Zoom reset" +msgstr "Reestablecer el zoom" + +#: src/utils/TranslationUtils.gd +msgid "Show grid" +msgstr "Mostrar cuadrícula" + +#: src/utils/TranslationUtils.gd +msgid "Show handles" +msgstr "Mostrar controladores" + +#: src/utils/TranslationUtils.gd +msgid "Show rasterized SVG" +msgstr "Mostrar SVG rasterizado" + +#: src/utils/TranslationUtils.gd +msgid "Toggle snapping" +msgstr "Alternar ajuste" + +#: src/utils/TranslationUtils.gd +msgid "Load reference image" +msgstr "Cargar imagen de referencia" + +#: src/utils/TranslationUtils.gd +msgid "Show reference image" +msgstr "Mostrar imagen de referencia" + +#: src/utils/TranslationUtils.gd +msgid "Overlay reference image" +msgstr "Superponer imagen de referencia" + +#: src/utils/TranslationUtils.gd +msgid "View debug information" +msgstr "Ver información de depuración" + +#: src/utils/TranslationUtils.gd +msgid "Settings" +msgstr "Configuración" + +#: src/utils/TranslationUtils.gd +msgid "Open Settings menu" +msgstr "Abrir el menú Configuración" + +#: src/utils/TranslationUtils.gd +msgid "About…" +msgstr "Acerca de…" + +#: src/utils/TranslationUtils.gd +msgid "Open About menu" +msgstr "Abrir el menú Acerca de" + +#: src/utils/TranslationUtils.gd +msgid "Donate…" +msgstr "Donar…" + +#: src/utils/TranslationUtils.gd +msgid "Open Donate menu" +msgstr "Abrir el menú Donar" + +#: src/utils/TranslationUtils.gd +msgid "GodSVG repository" +msgstr "Repositorio de GodSVG" + +#: src/utils/TranslationUtils.gd +msgid "Open GodSVG repository" +msgstr "Abrir repositorio de GodSVG" + +#: src/utils/TranslationUtils.gd +msgid "GodSVG website" +msgstr "Sitio web de GodSVG" + +#: src/utils/TranslationUtils.gd +msgid "Open GodSVG website" +msgstr "Abrir sitio web de GodSVG" + +#: src/utils/TranslationUtils.gd +msgid "Check for updates" +msgstr "Buscar actualizaciones" + +#: src/utils/TranslationUtils.gd +msgid "Quit the application" +msgstr "Salir de la aplicación" + +#: src/utils/TranslationUtils.gd +msgid "Toggle fullscreen" +msgstr "Alternar pantalla completa" + +#: src/utils/TranslationUtils.gd +msgid "Move to" +msgstr "Mover a" + +#: src/utils/TranslationUtils.gd +msgid "Line to" +msgstr "Alinear a" + +#: src/utils/TranslationUtils.gd +msgid "Horizontal Line to" +msgstr "Línea horizontal a" + +#: src/utils/TranslationUtils.gd +msgid "Vertical Line to" +msgstr "Línea vertical a" + +#: src/utils/TranslationUtils.gd +msgid "Close Path" +msgstr "Cerrar trazo" + +#: src/utils/TranslationUtils.gd +msgid "Elliptical Arc to" +msgstr "Arco elíptico a" + +#: src/utils/TranslationUtils.gd +msgid "Quadratic Bezier to" +msgstr "Bezier cuadrática a" + +#: src/utils/TranslationUtils.gd +msgid "Shorthand Quadratic Bezier to" +msgstr "Bezier cuadrática abreviada a" + +#: src/utils/TranslationUtils.gd +msgid "Cubic Bezier to" +msgstr "Bezier cúbica a" + +#: src/utils/TranslationUtils.gd +msgid "Shorthand Cubic Bezier to" +msgstr "Beziers cúbica abreviada a" + +#: src/utils/TranslationUtils.gd +msgid "Absolute" +msgstr "Absoluto" + +#: src/utils/TranslationUtils.gd +msgid "Code editor" +msgstr "Editor de código" + +#: src/utils/TranslationUtils.gd +msgid "Inspector" +msgstr "Inspector" + +#. The viewport is the area where the graphic is displayed. In similar applications, it's often called the canvas. +#: src/utils/TranslationUtils.gd +msgid "Viewport" +msgstr "Ventana gráfica" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Select {format} files" +msgstr "Selecciona un archivo {format}" + +#: src/utils/TranslationUtils.gd +msgid "Select an image" +msgstr "Selecciona una imagen" + +#: src/utils/TranslationUtils.gd +msgid "Select an {format} file" +msgstr "Selecciona un archivo {format}" + +#: src/utils/TranslationUtils.gd +msgid "Save the {format} file" +msgstr "Guardar el archivo {format}" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." +msgstr "" +"La extensión del archivo está vacía. Solo se admiten los archivos " +"{extension_list}." + +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "La extensión de archivo {extension} no es compatible con esta operación. " +#~ "Solo se admiten los archivos {extension_list}." diff --git a/translations/et.po b/translations/et.po index b9d5553..12af165 100644 --- a/translations/et.po +++ b/translations/et.po @@ -71,13 +71,13 @@ msgstr "" "Graafika saab eksportida ainult SVG-na, sest selle suurus pole defineeritud." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Hoiatus!" #: src/autoload/State.gd src/utils/TranslationUtils.gd msgid "Close tab" -msgstr "Sulge kaart" +msgstr "Sulge vahekaart" #: src/autoload/State.gd msgid "Restore" @@ -104,8 +104,8 @@ msgid "" "The tab is bound to the file path {file_path}. Do you want to restore the " "SVG from this path?" msgstr "" -"Kaart on seotud failiga asukohas {file_path}. Kas te soovite taastada SVG " -"sellest failist?" +"See vahekaart on seotud failiga asukohas {file_path}. Kas te soovite " +"taastada SVG sellest failist?" #: src/config_classes/Formatter.gd msgid "Compact" @@ -251,7 +251,7 @@ msgstr "Kleebi võrdluspilt" #: src/ui_parts/donate_menu.gd msgid "Links to donation platforms" -msgstr "" +msgstr "Annetusplatvormide lingid" #: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd #: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd @@ -299,6 +299,10 @@ msgstr "Laius" msgid "Height" msgstr "Kõrgus" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Kopeeri" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "Eelvaade on piiratud suurusele {dimensions}" @@ -327,14 +331,14 @@ msgstr "Lülita peidetud failide kuvamine sisse" msgid "Search files" msgstr "Otsi faile" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Vali" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Salvesta" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Vali" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Asukoht" @@ -544,7 +548,7 @@ msgstr "Sisend" #: src/ui_parts/settings_menu.gd msgid "Close tabs with middle mouse button" -msgstr "Sulge kaarte keskmise hiireklahviga" +msgstr "Sulge vahekaarte keskmise hiireklahviga" #: src/ui_parts/settings_menu.gd msgid "Invert zoom direction" @@ -777,7 +781,7 @@ msgstr "Kiirklahvida seadistamise paneel" #: src/ui_parts/tab_bar.gd src/utils/TranslationUtils.gd msgid "Create a new tab" -msgstr "Loo uus kaart" +msgstr "Loo uus vahekaart" #: src/ui_parts/tab_bar.gd msgid "Scroll backwards" @@ -791,7 +795,7 @@ msgstr "Keri edasi" msgid "This SVG is not bound to a file location yet." msgstr "See SVG ei ole veel seotud failiasukohaga." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Proovi uuesti" @@ -813,18 +817,16 @@ msgid "Update check failed" msgstr "Uuenduste otsimine ebaõnnestus" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "View all releases" -msgstr "Näita eelväljaandeid" +msgstr "Näita kõiki väljaandeid" #: src/ui_parts/update_menu.gd msgid "GodSVG is up-to-date." msgstr "GodSVG on ajakohane." #: src/ui_parts/update_menu.gd -#, fuzzy msgid "New versions available!" -msgstr "Uued versioonid" +msgstr "Uuem versioon on saadaval!" #: src/ui_widgets/choose_name_dialog.gd msgid "Create" @@ -952,13 +954,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG ei tunne seda atribuuti" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." -msgstr "Faili avamine ebaõnnestus." +msgid "The following files were discarded:" +msgstr "Järgnevad failid hüljati:" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "{file_path} hüljati." + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "Hüljatud failid" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "Jätka" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." +msgstr "{file_path} ei saanud avada." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Kontrollige, kas fail asub veel valitud asukohas." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "Jätka ülejäänud failide importimisega?" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "Jätka kõikide failide jaoks, mida ei saa avada" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Salvesta fail?" @@ -980,8 +1006,8 @@ msgid "Don't save" msgstr "Ära salvesta" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." -msgstr "Imporditud fail on juba avatud GodSVGs." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "{file_path} on juba avatud GodSVGs." #: src/utils/FileUtils.gd msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1013,17 +1039,25 @@ msgstr "Sulge kaardid paremal" msgid "Close all other tabs" msgstr "Sulge teised kaardid" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "Sulge tühjad kaardid" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "Sulge salvestatud kaardid" + #: src/utils/TranslationUtils.gd msgid "Create tab" -msgstr "Uus kaart" +msgstr "Uus vahekaart" #: src/utils/TranslationUtils.gd msgid "Select the next tab" -msgstr "Vali järgmine kaart" +msgstr "Vali järgmine vahekaart" #: src/utils/TranslationUtils.gd msgid "Select the previous tab" -msgstr "Vali eelmine kaart" +msgstr "Vali eelmine vahekaart" #: src/utils/TranslationUtils.gd msgid "Optimize" @@ -1069,10 +1103,6 @@ msgstr "Võta tagasi" msgid "Redo" msgstr "Tee uuesti" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Kopeeri" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Kleebi" @@ -1270,6 +1300,10 @@ msgstr "Inspektor" msgid "Viewport" msgstr "Vaade" +#: src/utils/TranslationUtils.gd +msgid "Select {format} files" +msgstr "Vali {format} faile" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Vali pilt" @@ -1283,16 +1317,15 @@ msgid "Save the {format} file" msgstr "Salvesta {format} fail" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"Faililaiend {extension} ei ole selle operatsiooni jaoks toetatud. Toetatud " -"on {extension_list} failid." +msgid "Only {extension_list} files are supported for this operation." +msgstr "Ainult {extension_list} failid on toetatud selle tehingu jaoks." -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." -msgstr "Faililaiend on tühi. Ainult {extension_list} on toetatud." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "Faililaiend {extension} ei ole selle operatsiooni jaoks toetatud. " +#~ "Toetatud on {extension_list} failid." #~ msgid "Save the .\"{format}\" file" #~ msgstr "Salvesta .\"{format}\" fail" diff --git a/translations/fr.po b/translations/fr.po index 8f76a58..1073617 100644 --- a/translations/fr.po +++ b/translations/fr.po @@ -71,7 +71,7 @@ msgstr "" "Le graphique ne peut être exporté qu'en SVG car sa taille n'est pas définie." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Alerte !" @@ -301,6 +301,10 @@ msgstr "Largeur" msgid "Height" msgstr "Hauteur" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Copier" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "La taille de l'image de prévisualisation est limitée à {dimensions}" @@ -329,14 +333,14 @@ msgstr "Activer/Désactiver la visibilité des fichiers cachés" msgid "Search files" msgstr "Rechercher des fichiers" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Sélectionner" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Enregistrer" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Sélectionner" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Chemin" @@ -793,7 +797,7 @@ msgstr "Défiler en avant" msgid "This SVG is not bound to a file location yet." msgstr "Ce SVG n'est pas encore lié à un emplacement de fichier." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Réessayer" @@ -954,13 +958,38 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG ne reconnaît pas cet attribut" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} couldn't be opened." msgstr "Le fichier n'a pas pu être ouvert." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Vérifiez si le fichier existe encore au chemin selectionné." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Enregistrer le fichier ?" @@ -982,7 +1011,8 @@ msgid "Don't save" msgstr "Ne pas enregistrer" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +#, fuzzy +msgid "{file_path} is already being edited inside GodSVG." msgstr "Le fichier importé est déjà en cours de modification dans GodSVG." #: src/utils/FileUtils.gd @@ -1016,6 +1046,16 @@ msgstr "Fermer les onglets sur la droite" msgid "Close all other tabs" msgstr "Fermer les autres onglets" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close empty tabs" +msgstr "Fermer l'onglet" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close saved tabs" +msgstr "Fermer les autres onglets" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Créer un onglet" @@ -1074,10 +1114,6 @@ msgstr "Annuler" msgid "Redo" msgstr "Rétablir" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Copier" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Coller" @@ -1278,6 +1314,11 @@ msgstr "" msgid "Viewport" msgstr "Affichage" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Select {format} files" +msgstr "Sélectionner un fichier XML" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Sélectionner une image" @@ -1293,19 +1334,19 @@ msgid "Save the {format} file" msgstr "Enregistrer le fichier .\"{format}\"" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"L'extension de fichier {extension} n'est pas supportée pour cette opération. " -"Seuls les fichiers {extension_list} sont supportés." - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." msgstr "" "L'extension de fichier est vide. Seuls les fichiers {extension_list} sont " "supportés." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "L'extension de fichier {extension} n'est pas supportée pour cette " +#~ "opération. Seuls les fichiers {extension_list} sont supportés." + #~ msgid "Save the .\"{format}\" file" #~ msgstr "Enregistrer le fichier .\"{format}\"" diff --git a/translations/nl.po b/translations/nl.po index 6d86e13..c378852 100644 --- a/translations/nl.po +++ b/translations/nl.po @@ -72,7 +72,7 @@ msgstr "" "niet gedefiniëerd." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Aandacht!" @@ -302,6 +302,10 @@ msgstr "Breedte" msgid "Height" msgstr "Hoogte" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Kopiëren" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "Voorbeeldafbeelding maat is beperkt tot {dimensions}" @@ -330,14 +334,14 @@ msgstr "De zichtbaarheit van verborgen bestanden omschakelen" msgid "Search files" msgstr "Bestanden zoeken" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Selecteren" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Opslaan" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Selecteren" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Pad" @@ -797,7 +801,7 @@ msgstr "Scroll vooruit" msgid "This SVG is not bound to a file location yet." msgstr "Deze SVG is nog niet gebonden aan een bestandslocatie." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Opnieuw proberen" @@ -958,13 +962,38 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG herkent deze attribuut niet" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} couldn't be opened." msgstr "Het bestand kon niet worden geopend." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Kijk na of het bestand nog steeds bestaat in het gekozen bestandspad" +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "sla het bestand op?" @@ -986,7 +1015,8 @@ msgid "Don't save" msgstr "Niet opslaan." #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +#, fuzzy +msgid "{file_path} is already being edited inside GodSVG." msgstr "Het geïmporteerde bestand is al geöpend in GodSVG" #: src/utils/FileUtils.gd @@ -1020,6 +1050,16 @@ msgstr "Sluit rechter tabbladen" msgid "Close all other tabs" msgstr "Kopiëer alle tekst" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close empty tabs" +msgstr "Tabblad sluiten" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close saved tabs" +msgstr "Kopiëer alle tekst" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Tabblad creëren" @@ -1078,10 +1118,6 @@ msgstr "Ongedaan maken" msgid "Redo" msgstr "Opnieuw doen" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Kopiëren" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Plakken" @@ -1282,6 +1318,11 @@ msgstr "" msgid "Viewport" msgstr "Zicht" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Select {format} files" +msgstr "Kies een XML bestand uit" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Kies een afbeedling" @@ -1297,19 +1338,19 @@ msgid "Save the {format} file" msgstr "Bewaar het .\"{format}\" bestand" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"Deze operatie is niet ondersteund voor de bestandsextensie {extension}." -"Alleen {extension_list} bestanden zijn ondersteund." - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." msgstr "" "Deze bestandsextensie is leeg. Alleen {extension_list} bestanden zijn " "ondersteund." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "Deze operatie is niet ondersteund voor de bestandsextensie {extension}." +#~ "Alleen {extension_list} bestanden zijn ondersteund." + #~ msgid "Save the .\"{format}\" file" #~ msgstr "Bewaar het .\"{format}\" bestand" diff --git a/translations/pt_BR.po b/translations/pt_BR.po index 1b1b6e3..ae4b2ed 100644 --- a/translations/pt_BR.po +++ b/translations/pt_BR.po @@ -10,7 +10,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 3.5\n" +"X-Generator: Poedit 3.6\n" msgid "translation-credits" msgstr "Felipe Sena Costa " @@ -72,7 +72,7 @@ msgstr "" "definido." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Alerta!" @@ -85,9 +85,8 @@ msgid "Restore" msgstr "Restaurar" #: src/autoload/State.gd -#, fuzzy msgid "View in Inspector" -msgstr "Ver na Lista" +msgstr "Ver no Inspetor" #: src/autoload/State.gd msgid "Convert To" @@ -249,13 +248,12 @@ msgid "Snap size" msgstr "Tamanho de encaixe" #: src/ui_parts/display.gd -#, fuzzy msgid "Paste reference image" msgstr "Carregar imagem de referência" #: src/ui_parts/donate_menu.gd msgid "Links to donation platforms" -msgstr "" +msgstr "Links para plataforma de doação" #: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd #: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd @@ -303,6 +301,10 @@ msgstr "Largura" msgid "Height" msgstr "Altura" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Copiar" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "" @@ -332,14 +334,14 @@ msgstr "Alternar a visibilidade de arquivos escondidos" msgid "Search files" msgstr "Procurar arquivos" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Selecionar" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Salvar" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Selecionar" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Caminho" @@ -411,11 +413,11 @@ msgstr "Adicionar elemento" #. Refers to the zero, one, or multiple UI parts to not be shown in the final layout. It's of plural cardinality. #: src/ui_parts/layout_popup.gd msgid "Excluded" -msgstr "" +msgstr "Excluídos" #: src/ui_parts/layout_popup.gd msgid "Drag and drop to change the layout" -msgstr "" +msgstr "Arraste e solte para modificar o layout" #: src/ui_parts/mac_menu.gd src/ui_parts/settings_menu.gd msgid "File" @@ -453,6 +455,10 @@ msgstr "Atalhos" msgid "Theming" msgstr "Temas" +#: src/ui_parts/settings_menu.gd +msgid "Tab bar" +msgstr "Barra de guias" + #: src/ui_parts/settings_menu.gd msgid "Other" msgstr "Outro" @@ -528,9 +534,8 @@ msgid "Background color" msgstr "Cor do plano de fundo" #: src/ui_parts/settings_menu.gd -#, fuzzy msgid "Grid color" -msgstr "Cor válida" +msgstr "Cor da grade" #: src/ui_parts/settings_menu.gd msgid "Valid color" @@ -546,7 +551,7 @@ msgstr "Entrada" #: src/ui_parts/settings_menu.gd msgid "Close tabs with middle mouse button" -msgstr "" +msgstr "Fechar guias com o botão do meio do mouse" #: src/ui_parts/settings_menu.gd msgid "Invert zoom direction" @@ -720,6 +725,8 @@ msgid "" "If turned on, clicking on a tab with the middle mouse button closes the tab. " "If turned off, it focuses the tab instead." msgstr "" +"Se ligado, clicar em uma guia com o botão do meio do mouse fechará a guia. " +"Se desligado, irá focar na guia." #: src/ui_parts/settings_menu.gd msgid "Swaps the scroll directions for zooming in and zooming out." @@ -734,7 +741,6 @@ msgstr "" "viewport quando movimentando a visualização." #: src/ui_parts/settings_menu.gd -#, fuzzy msgid "" "If turned on, scrolling pans the view. To zoom, hold CTRL while scrolling." msgstr "" @@ -750,13 +756,12 @@ msgstr "" "desabilitado, usa o diálogo de arquivo embutido no GodSVG." #: src/ui_parts/settings_menu.gd -#, fuzzy msgid "" "If turned off, the window title remains as \"GodSVG\" without including the " "current file." msgstr "" -"Se desabilitado, o título da janela permanecerá como \"GodSVG\" independente " -"do arquivo aberto." +"Se desabilitado, o título da janela permanecerá como \"GodSVG\" sem incluir " +"o nome do arquivo aberto." #: src/ui_parts/settings_menu.gd msgid "Changes the visual size and grabbing area of handles." @@ -794,12 +799,11 @@ msgstr "Rolar para frente" msgid "This SVG is not bound to a file location yet." msgstr "Esse SVG ainda não está vinculado a um local de arquivo." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Tentar novamente" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "Show prereleases" msgstr "Incluir pré-lançamentos" @@ -817,18 +821,16 @@ msgid "Update check failed" msgstr "Verificação de atualização falhou" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "View all releases" -msgstr "Incluir pré-lançamentos" +msgstr "Ver todos os lançamentos" #: src/ui_parts/update_menu.gd msgid "GodSVG is up-to-date." msgstr "GodSVG está atualizado." #: src/ui_parts/update_menu.gd -#, fuzzy msgid "New versions available!" -msgstr "Novas versões" +msgstr "Novas versões disponíveis!" #: src/ui_widgets/choose_name_dialog.gd msgid "Create" @@ -956,55 +958,78 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "O GodSVG não reconhece este atributo" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." -msgstr "Este arquivo não pode ser aberto." +msgid "The following files were discarded:" +msgstr "Os seguintes arquivos foram descartados:" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "{file_path} foi descartado." + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "Arquivos descartados" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "Proceder" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." +msgstr "{file_path} não pode ser aberto." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Verificar se o arquivo ainda existe no local de arquivo selecionado." #: src/utils/FileUtils.gd -#, fuzzy +msgid "Proceed with importing the rest of the files?" +msgstr "Prosseguir com a importação do restante dos arquivos?" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "Prosseguir para todos os arquivos que não podem ser abertos" + +#: src/utils/FileUtils.gd msgid "Save the file?" -msgstr "Salvar o arquivo .\"{format}\"" +msgstr "Salvar o arquivo?" #: src/utils/FileUtils.gd -#, fuzzy msgid "Do you want to save this file?" -msgstr "Você deseja prosseguir?" +msgstr "Você deseja salvar este arquivo?" #: src/utils/FileUtils.gd msgid "Save the changes?" -msgstr "" +msgstr "Salvar as alterações?" #: src/utils/FileUtils.gd msgid "Your changes will be lost if you don't save them." -msgstr "" +msgstr "Suas alterações serão perdidas se você não as salvar." #: src/utils/FileUtils.gd msgid "Don't save" -msgstr "" +msgstr "Não salvar" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." -msgstr "O arquivo importado já está sendo editado dentro do GodSVG." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "{file_path} já está sendo editado dentro do GodSVG." #: src/utils/FileUtils.gd msgid "If you want to revert your edits since the last save, use {reset_svg}." msgstr "" +"Se você deseja reverter as suas edições desde o último salvamento, use " +"{reset_svg}." #: src/utils/FileUtils.gd msgid "Do you want to save the changes made to {file_name}?" -msgstr "" +msgstr "Você deseja salvar as alterações feitas em {file_name}?" #: src/utils/TranslationUtils.gd msgid "Save SVG" msgstr "Salvar SVG" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Save SVG as" -msgstr "Salvar SVG como…" +msgstr "Salvar SVG como" #: src/utils/TranslationUtils.gd msgid "Close tabs to the left" @@ -1018,6 +1043,14 @@ msgstr "Fechar abas à direita" msgid "Close all other tabs" msgstr "Fechar todas as outras abas" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "Fechar abas vazias" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "Fechar abas salvas" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Criar aba" @@ -1035,18 +1068,16 @@ msgid "Optimize" msgstr "Otimizar" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Optimize SVG" -msgstr "Otimizar" +msgstr "Otimizar SVG" #: src/utils/TranslationUtils.gd msgid "Copy all text" msgstr "Copiar todo texto" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Copy the SVG text" -msgstr "Copiar todo texto" +msgstr "Copiar todo texto do SVG" #: src/utils/TranslationUtils.gd msgid "Reset SVG" @@ -1076,10 +1107,6 @@ msgstr "Desfazer" msgid "Redo" msgstr "Refazer" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Copiar" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Colar" @@ -1105,7 +1132,6 @@ msgid "Delete the selection" msgstr "Excluir a seleção" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Move up" msgstr "Mover para acima" @@ -1114,7 +1140,6 @@ msgid "Move the selection up" msgstr "Mover a seleção para cima" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Move down" msgstr "Mover para abaixo" @@ -1220,7 +1245,7 @@ msgstr "Fechar programa" #: src/utils/TranslationUtils.gd msgid "Toggle fullscreen" -msgstr "" +msgstr "Alternar tela cheia" #: src/utils/TranslationUtils.gd msgid "Move to" @@ -1268,46 +1293,46 @@ msgstr "Absoluto" #: src/utils/TranslationUtils.gd msgid "Code editor" -msgstr "" +msgstr "Editor de código" #: src/utils/TranslationUtils.gd msgid "Inspector" -msgstr "" +msgstr "Inspetor" #. The viewport is the area where the graphic is displayed. In similar applications, it's often called the canvas. #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Viewport" -msgstr "Visualizar" +msgstr "Canvas" + +#: src/utils/TranslationUtils.gd +msgid "Select {format} files" +msgstr "Selecionar arquivos do tipo {format}" #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Selecionar uma imagem" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Select an {format} file" -msgstr "Selecionar um arquivo XML" +msgstr "Selecionar um arquivo do tipo {format}" #: src/utils/TranslationUtils.gd -#, fuzzy msgid "Save the {format} file" -msgstr "Salvar o arquivo .\"{format}\"" +msgstr "Salvar o arquivo do tipo {format}" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"A extensão de arquivo {extension} não é compatível com esta operação. " -"Somente arquivos {extension_list} são suportados." - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +msgid "Only {extension_list} files are supported for this operation." msgstr "" "A extensão de arquivo está vazia. Somente arquivos {extension_list} são " "suportados." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "A extensão de arquivo {extension} não é compatível com esta operação. " +#~ "Somente arquivos {extension_list} são suportados." + #~ msgid "Save the .\"{format}\" file" #~ msgstr "Salvar o arquivo .\"{format}\"" diff --git a/translations/ru.po b/translations/ru.po index f6ced34..a295f40 100644 --- a/translations/ru.po +++ b/translations/ru.po @@ -73,7 +73,7 @@ msgstr "" "заданы." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Внимание!" @@ -248,13 +248,12 @@ msgid "Snap size" msgstr "Размер привязки" #: src/ui_parts/display.gd -#, fuzzy msgid "Paste reference image" -msgstr "Загрузить референс" +msgstr "Вставить референсое изображение" #: src/ui_parts/donate_menu.gd msgid "Links to donation platforms" -msgstr "" +msgstr "Ссылки на платформы для пожертвований" #: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd #: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd @@ -302,6 +301,10 @@ msgstr "Ширина" msgid "Height" msgstr "Высота" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Скопировать" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "" @@ -331,14 +334,14 @@ msgstr "Переключить видимость скрытых файлов" msgid "Search files" msgstr "Искать файлы" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Выбрать" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Сохранить" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Выбрать" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Путь" @@ -797,7 +800,7 @@ msgstr "Прокрутить вперед" msgid "This SVG is not bound to a file location yet." msgstr "Это SVG изображение еще не связано с расположением файла." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Попробовать еще раз" @@ -819,18 +822,16 @@ msgid "Update check failed" msgstr "Не удалось проверить обновления" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "View all releases" -msgstr "Показывать тестовые сборки" +msgstr "Показать все сборки" #: src/ui_parts/update_menu.gd msgid "GodSVG is up-to-date." msgstr "Установлена последняя версия GodSVG." #: src/ui_parts/update_menu.gd -#, fuzzy msgid "New versions available!" -msgstr "Новые версии" +msgstr "Доступна новая версия!" #: src/ui_widgets/choose_name_dialog.gd msgid "Create" @@ -958,13 +959,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG не может распознать этот атрибут" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." -msgstr "Файл невозможно открыть." +msgid "The following files were discarded:" +msgstr "Следующие файлы были отклонены:" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "{file_path} был отклонен." + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "Отклоненные файлы" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "Продолжить" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." +msgstr "Невозможно открыть {file_path} файл." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Проверьте, что файл все еще существует по выбранному пути." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "Продолжить импортировать остальные файлы?" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "Продолжить для всех файлов, которые не могут быть открыты" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Сохранить файл?" @@ -986,8 +1011,8 @@ msgid "Don't save" msgstr "Не сохранять" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." -msgstr "Импортированный файл уже редактируется в GodSVG." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "Файл {file_path} уже редактируется внутри GodSVG." #: src/utils/FileUtils.gd msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1019,6 +1044,14 @@ msgstr "Закрыть вкладки справа" msgid "Close all other tabs" msgstr "Закрыть все остальные вкладки" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "Закрыть пустые вкладки" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "Закрыть сохраненные вкладки" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Создать вкладку" @@ -1075,10 +1108,6 @@ msgstr "Отменить" msgid "Redo" msgstr "Вернуть" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Скопировать" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Вставить" @@ -1217,7 +1246,7 @@ msgstr "Выйти из приложения" #: src/utils/TranslationUtils.gd msgid "Toggle fullscreen" -msgstr "" +msgstr "Переключить полный экран" #: src/utils/TranslationUtils.gd msgid "Move to" @@ -1276,6 +1305,10 @@ msgstr "Инспектор" msgid "Viewport" msgstr "Окно просмотра" +#: src/utils/TranslationUtils.gd +msgid "Select {format} files" +msgstr "Выбрать {format} файлы" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Выбрать изображение" @@ -1289,16 +1322,17 @@ msgid "Save the {format} file" msgstr "Сохранить {format} файл" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." +msgid "Only {extension_list} files are supported for this operation." msgstr "" -"Расширение файла {extension} не поддерживается для этой операции. Только " -"{extension_list} файлы поддерживаются." +"Только файлы с расширениями {extension_list} поддерживаются для этой " +"операции." -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." -msgstr "Расширение файла пустое. Поддерживаются только {extension_list} файлы." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "Расширение файла {extension} не поддерживается для этой операции. Только " +#~ "{extension_list} файлы поддерживаются." #~ msgid "Save the .\"{format}\" file" #~ msgstr "Сохранить .\"{format}\" файл" diff --git a/translations/uk.po b/translations/uk.po index a860f96..1aa97db 100644 --- a/translations/uk.po +++ b/translations/uk.po @@ -72,7 +72,7 @@ msgstr "" "завданий." #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "Увага!" @@ -247,13 +247,12 @@ msgid "Snap size" msgstr "Розмір прилипання" #: src/ui_parts/display.gd -#, fuzzy msgid "Paste reference image" -msgstr "Завантажити еталонне зображення" +msgstr "Вставити еталонне зображення" #: src/ui_parts/donate_menu.gd msgid "Links to donation platforms" -msgstr "" +msgstr "Посилання на платформи для пожертв" #: src/ui_parts/donate_menu.gd src/ui_parts/export_menu.gd #: src/ui_parts/import_warning_menu.gd src/ui_widgets/choose_name_dialog.gd @@ -301,6 +300,10 @@ msgstr "Ширина" msgid "Height" msgstr "Висота" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "Скопіювати" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "Розмір попереднього перегляду зображення обмежено до {dimensions}" @@ -329,14 +332,14 @@ msgstr "Перемикнути видимість прихованих файл msgid "Search files" msgstr "Пошук файлів" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "Обрати" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "Зберегти" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "Обрати" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "Шлях" @@ -799,7 +802,7 @@ msgstr "Гортати вперед" msgid "This SVG is not bound to a file location yet." msgstr "Це SVG зображення поки що не пов'язано з розташуванням файла." -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "Спробувати ще раз" @@ -821,18 +824,16 @@ msgid "Update check failed" msgstr "Не вдалося перевірити оновлення" #: src/ui_parts/update_menu.gd -#, fuzzy msgid "View all releases" -msgstr "Показати тестові версії" +msgstr "Показати усі версії" #: src/ui_parts/update_menu.gd msgid "GodSVG is up-to-date." msgstr "GodSVG оновлено до останньої версії." #: src/ui_parts/update_menu.gd -#, fuzzy msgid "New versions available!" -msgstr "Нові версії" +msgstr "Доступна нова версія!" #: src/ui_widgets/choose_name_dialog.gd msgid "Create" @@ -960,13 +961,37 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG не може розпізнати цей атрибут" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." -msgstr "Цей файл неможливо відкрити." +msgid "The following files were discarded:" +msgstr "Наступні файли були відкинуті:" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "{file_path} було відкинуто." + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "Відкинуті файли" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "Продовжити" + +#: src/utils/FileUtils.gd +msgid "{file_path} couldn't be opened." +msgstr "Файл {file_path} неможливо відкрити." #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "Перевірити, чи файл все щє існує по обраному шляху." +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "Продовжити імпортуючи інші файли?" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "Продовжити для усіх файлів, які неможливо відкрити" + #: src/utils/FileUtils.gd msgid "Save the file?" msgstr "Зберегти файл?" @@ -988,8 +1013,8 @@ msgid "Don't save" msgstr "Не зберігати" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." -msgstr "Імпортований файл вже редагується в GodSVG." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "Файл {file_path} вже редагується в GodSVG." #: src/utils/FileUtils.gd msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1021,6 +1046,14 @@ msgstr "Закрити усі вкладки справа" msgid "Close all other tabs" msgstr "Закрити усі інші вкладки" +#: src/utils/TranslationUtils.gd +msgid "Close empty tabs" +msgstr "Закрити порожні вкладки" + +#: src/utils/TranslationUtils.gd +msgid "Close saved tabs" +msgstr "Закрити збережені вкладки" + #: src/utils/TranslationUtils.gd msgid "Create tab" msgstr "Створити вкладку" @@ -1077,10 +1110,6 @@ msgstr "Скасувати" msgid "Redo" msgstr "Повернути" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "Скопіювати" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "Вставити" @@ -1220,7 +1249,7 @@ msgstr "Вийти із додатку" #: src/utils/TranslationUtils.gd msgid "Toggle fullscreen" -msgstr "" +msgstr "Перемикнути повноекранний режим" #: src/utils/TranslationUtils.gd msgid "Move to" @@ -1279,6 +1308,10 @@ msgstr "Інспектор" msgid "Viewport" msgstr "Вікно перегляду" +#: src/utils/TranslationUtils.gd +msgid "Select {format} files" +msgstr "Обрати {format} файли" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "Обрати зображення" @@ -1292,16 +1325,16 @@ msgid "Save the {format} file" msgstr "Зберегти {format} файл" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." +msgid "Only {extension_list} files are supported for this operation." msgstr "" -"Розширення файлу {extension} не підтримується для цієї операції. Тільки " -"{extension_list} файли підтримуються." +"Тільки файли з розширеннями {extension_list} підтримуються для цієї операції." -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." -msgstr "Розширення файлу порожнє. Тільки {extension_list} файли підтримуються." +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "Розширення файлу {extension} не підтримується для цієї операції. Тільки " +#~ "{extension_list} файли підтримуються." #~ msgid "Save the .\"{format}\" file" #~ msgstr "Зберегти .\"{format}\" файл" diff --git a/translations/zh_CN.po b/translations/zh_CN.po index 5c31663..287c0d9 100644 --- a/translations/zh_CN.po +++ b/translations/zh_CN.po @@ -69,7 +69,7 @@ msgid "" msgstr "此图像只能以 SVG 格式导出,因为其大小未定义。您确定要继续吗?" #: src/autoload/State.gd src/ui_parts/good_file_dialog.gd -#: src/ui_widgets/alert_dialog.gd +#: src/ui_widgets/alert_dialog.gd src/utils/FileUtils.gd msgid "Alert!" msgstr "警告!" @@ -299,6 +299,10 @@ msgstr "宽度" msgid "Height" msgstr "高度" +#: src/ui_parts/export_menu.gd src/utils/TranslationUtils.gd +msgid "Copy" +msgstr "复制" + #: src/ui_parts/export_menu.gd msgid "Preview image size is limited to {dimensions}" msgstr "预览图像大小被限制到 {dimensions}" @@ -327,14 +331,14 @@ msgstr "切换隐藏文件可见性" msgid "Search files" msgstr "搜索文件" -#: src/ui_parts/good_file_dialog.gd -msgid "Select" -msgstr "选择" - #: src/ui_parts/good_file_dialog.gd src/utils/FileUtils.gd msgid "Save" msgstr "保存" +#: src/ui_parts/good_file_dialog.gd +msgid "Select" +msgstr "选择" + #: src/ui_parts/good_file_dialog.gd msgid "Path" msgstr "路径" @@ -789,7 +793,7 @@ msgstr "" msgid "This SVG is not bound to a file location yet." msgstr "" -#: src/ui_parts/update_menu.gd +#: src/ui_parts/update_menu.gd src/utils/FileUtils.gd msgid "Retry" msgstr "重试" @@ -953,13 +957,38 @@ msgid "GodSVG doesn’t recognize this attribute" msgstr "GodSVG 无法识别该属性" #: src/utils/FileUtils.gd -msgid "The file couldn't be opened." +msgid "The following files were discarded:" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "{file_path} was discarded." +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Discarded files" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed" +msgstr "" + +#: src/utils/FileUtils.gd +#, fuzzy +msgid "{file_path} couldn't be opened." msgstr "无法打开文件。" #: src/utils/FileUtils.gd msgid "Check if the file still exists in the selected file path." msgstr "检查文件是否还存在于选中的路径。" +#: src/utils/FileUtils.gd +msgid "Proceed with importing the rest of the files?" +msgstr "" + +#: src/utils/FileUtils.gd +msgid "Proceed for all files that can't be opened" +msgstr "" + #: src/utils/FileUtils.gd #, fuzzy msgid "Save the file?" @@ -983,7 +1012,7 @@ msgid "Don't save" msgstr "" #: src/utils/FileUtils.gd -msgid "The imported file is already being edited inside GodSVG." +msgid "{file_path} is already being edited inside GodSVG." msgstr "" #: src/utils/FileUtils.gd @@ -1016,6 +1045,16 @@ msgstr "" msgid "Close all other tabs" msgstr "复制所有文本" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close empty tabs" +msgstr "闭合路径" + +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Close saved tabs" +msgstr "复制所有文本" + #: src/utils/TranslationUtils.gd #, fuzzy msgid "Create tab" @@ -1076,10 +1115,6 @@ msgstr "撤销" msgid "Redo" msgstr "恢复" -#: src/utils/TranslationUtils.gd -msgid "Copy" -msgstr "复制" - #: src/utils/TranslationUtils.gd msgid "Paste" msgstr "粘贴" @@ -1284,6 +1319,11 @@ msgstr "" msgid "Viewport" msgstr "视图" +#: src/utils/TranslationUtils.gd +#, fuzzy +msgid "Select {format} files" +msgstr "选择 XML 文件" + #: src/utils/TranslationUtils.gd msgid "Select an image" msgstr "选择图像" @@ -1299,16 +1339,16 @@ msgid "Save the {format} file" msgstr "保存 .\"{format}\" 文件" #: src/utils/TranslationUtils.gd -msgid "" -"The file extension {extension} is unsupported for this operation. Only " -"{extension_list} files are supported." -msgstr "" -"扩展名 {extension} 不支持此操作。只有 {extension_list} 内的文件受支持。" - -#: src/utils/TranslationUtils.gd -msgid "The file extension is empty. Only {extension_list} files are supported." +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." msgstr "文件扩展名为空。只有 {extension_list} 内的文件受支持。" +#~ msgid "" +#~ "The file extension {extension} is unsupported for this operation. Only " +#~ "{extension_list} files are supported." +#~ msgstr "" +#~ "扩展名 {extension} 不支持此操作。只有 {extension_list} 内的文件受支持。" + #~ msgid "Save the .\"{format}\" file" #~ msgstr "保存 .\"{format}\" 文件"