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}\" 文件"