From cfa26fc69bca88fc45bc1af983922839c7340d63 Mon Sep 17 00:00:00 2001 From: Anish Mishra Date: Fri, 15 Aug 2025 20:44:24 +0530 Subject: [PATCH 1/3] Sync with upstream --- .../icons/{backgrounds => }/Checkerboard.svg | 0 assets/icons/Checkerboard.svg.import | 18 + ...ttonBG.svg => CheckerboardColorButton.svg} | 0 .../icons/CheckerboardColorButton.svg.import | 18 + .../{backgrounds => }/CheckerboardMini.svg | 0 assets/icons/CheckerboardMini.svg.import | 18 + .../icons/backgrounds/Checkerboard.svg.import | 18 - .../backgrounds/CheckerboardMini.svg.import | 18 - .../backgrounds/ColorButtonBG.svg.import | 18 - src/autoload/Configs.gd | 65 ++- src/autoload/HandlerGUI.gd | 71 +-- src/autoload/State.gd | 157 ++----- src/config_classes/ConfigResource.gd | 8 +- src/config_classes/Formatter.gd | 10 +- src/config_classes/Palette.gd | 17 +- src/config_classes/SVGHighlighter.gd | 4 +- src/config_classes/SaveData.gd | 31 +- src/config_classes/TabData.gd | 27 +- src/data_classes/Attribute.gd | 19 +- src/data_classes/AttributeColor.gd | 14 +- src/data_classes/AttributeEnum.gd | 4 +- src/data_classes/AttributeHref.gd | 8 +- src/data_classes/AttributeID.gd | 4 +- src/data_classes/AttributeList.gd | 2 +- src/data_classes/AttributeNumeric.gd | 2 +- src/data_classes/AttributePathdata.gd | 5 +- src/data_classes/AttributeTransformList.gd | 2 +- src/data_classes/BasicXNode.gd | 1 + src/data_classes/ColorParser.gd | 121 ++--- src/data_classes/DB.gd | 66 +-- src/data_classes/Element.gd | 4 +- src/data_classes/ElementBaseGradient.gd | 3 +- src/data_classes/ElementCircle.gd | 2 +- src/data_classes/ElementEllipse.gd | 2 +- src/data_classes/ElementG.gd | 2 +- src/data_classes/ElementLine.gd | 9 +- src/data_classes/ElementLinearGradient.gd | 5 +- src/data_classes/ElementPath.gd | 8 +- src/data_classes/ElementPolygon.gd | 2 +- src/data_classes/ElementPolyline.gd | 2 +- src/data_classes/ElementRadialGradient.gd | 2 +- src/data_classes/ElementRect.gd | 2 +- src/data_classes/ElementRoot.gd | 2 +- src/data_classes/ElementSVG.gd | 2 +- src/data_classes/ElementStop.gd | 2 +- src/data_classes/ElementUse.gd | 2 +- src/data_classes/ListParser.gd | 5 +- src/data_classes/NumberParser.gd | 3 +- src/data_classes/NumstringParser.gd | 23 +- src/data_classes/SVGParser.gd | 122 ++--- src/shaders/animated_stroke.gdshader | 2 +- src/shaders/animated_stroke_static.gdshader | 2 +- src/ui_parts/code_editor.gd | 6 +- src/ui_parts/code_editor.tscn | 5 - src/ui_parts/debug_content.gd | 56 +++ src/ui_parts/debug_content.gd.uid | 1 + src/ui_parts/debug_content.tscn | 38 ++ src/ui_parts/display.gd | 119 ++--- src/ui_parts/display.tscn | 100 +---- src/ui_parts/display_texture.gd | 49 -- src/ui_parts/display_texture.gd.uid | 1 - src/ui_parts/editor_scene.gd | 27 -- src/ui_parts/element_container.gd | 2 +- src/ui_parts/export_menu.gd | 17 +- src/ui_parts/good_file_dialog.gd | 36 +- src/ui_parts/good_file_dialog.tscn | 12 - src/ui_parts/handles_manager.gd.uid | 1 - src/ui_parts/import_warning_menu.gd | 14 +- src/ui_parts/mac_menu.gd | 421 +++++++++--------- src/ui_parts/main_canvas.gd | 116 +++++ src/ui_parts/main_canvas.gd.uid | 1 + src/ui_parts/move_to_overlay.gd | 2 +- src/ui_parts/settings_menu.gd | 7 +- src/ui_parts/settings_menu.tscn | 2 - src/ui_parts/tab_bar.gd | 58 ++- src/ui_parts/top_app_bar.gd | 3 +- src/ui_parts/viewport.gd | 182 -------- src/ui_parts/viewport.gd.uid | 1 - src/ui_parts/viewport_container.gd | 130 ------ src/ui_parts/viewport_container.gd.uid | 1 - src/ui_widgets/Canvas.gd | 359 +++++++++++++++ src/ui_widgets/Canvas.gd.uid | 1 + src/ui_widgets/ContextPopup.gd | 16 +- src/ui_widgets/basic_xnode_frame.gd | 49 +- src/ui_widgets/color_configuration_popup.gd | 3 + src/ui_widgets/color_configuration_popup.tscn | 4 +- src/ui_widgets/color_edit.gd | 2 +- src/ui_widgets/color_field.gd | 18 +- src/ui_widgets/color_field_popup.gd | 4 +- src/ui_widgets/color_field_popup.tscn | 2 - src/ui_widgets/color_swatch.gd | 2 +- src/ui_widgets/color_swatch_config.gd | 2 +- src/ui_widgets/dropdown.gd | 2 + src/ui_widgets/dropdown.tscn | 2 - src/ui_widgets/element_frame.gd | 49 +- src/ui_widgets/enum_field.gd | 8 +- src/ui_widgets/flag_field.gd | 3 + src/ui_widgets/flag_field.tscn | 4 - src/ui_widgets/good_color_picker.gd | 37 +- src/ui_widgets/good_color_picker.tscn | 20 +- .../handles_manager.gd | 106 +++-- src/ui_widgets/handles_manager.gd.uid | 1 + src/ui_widgets/mini_number_field.gd | 4 +- src/ui_widgets/number_field.gd | 6 +- src/ui_widgets/number_field_with_slider.gd | 4 +- src/ui_widgets/palette_config.gd | 9 +- src/ui_widgets/pathdata_field.gd | 8 +- src/ui_widgets/preview_rect.gd | 2 +- src/ui_widgets/preview_rect.tscn | 2 +- src/ui_widgets/setting_frame.gd | 2 +- src/ui_widgets/settings_content_generic.gd | 246 +++++----- src/ui_widgets/settings_content_palettes.gd | 2 +- src/ui_widgets/transform_field.gd | 2 +- src/ui_widgets/viewport_controls.gd | 82 ++++ src/ui_widgets/viewport_controls.gd.uid | 1 + src/ui_widgets/zoom_menu.gd | 82 ---- src/ui_widgets/zoom_widget.gd | 47 ++ .../{zoom_menu.gd.uid => zoom_widget.gd.uid} | 0 .../{zoom_menu.tscn => zoom_widget.tscn} | 13 +- src/utils/AttributeFieldBuilder.gd | 2 +- src/utils/FileUtils.gd | 38 +- src/utils/ImageExportData.gd | 2 +- src/utils/ThemeUtils.gd | 24 + src/utils/TranslationUtils.gd | 26 +- src/utils/Translator.gd | 4 +- src/utils/Utils.gd | 10 +- src/utils/Utils64Bit.gd | 19 +- src/utils/XIDUtils.gd | 40 +- translations/GodSVG.pot | 44 +- translations/bg.po | 44 +- translations/de.po | 48 +- translations/en.po | 44 +- translations/es.po | 45 +- translations/et.po | 95 ++-- translations/fr.po | 99 ++-- translations/nl.po | 223 ++++------ translations/pt_BR.po | 95 ++-- translations/ru.po | 97 ++-- translations/uk.po | 95 ++-- translations/zh_CN.po | 55 +-- 140 files changed, 2394 insertions(+), 2323 deletions(-) rename assets/icons/{backgrounds => }/Checkerboard.svg (100%) create mode 100644 assets/icons/Checkerboard.svg.import rename assets/icons/{backgrounds/ColorButtonBG.svg => CheckerboardColorButton.svg} (100%) create mode 100644 assets/icons/CheckerboardColorButton.svg.import rename assets/icons/{backgrounds => }/CheckerboardMini.svg (100%) create mode 100644 assets/icons/CheckerboardMini.svg.import delete mode 100644 assets/icons/backgrounds/Checkerboard.svg.import delete mode 100644 assets/icons/backgrounds/CheckerboardMini.svg.import delete mode 100644 assets/icons/backgrounds/ColorButtonBG.svg.import create mode 100644 src/ui_parts/debug_content.gd create mode 100644 src/ui_parts/debug_content.gd.uid create mode 100644 src/ui_parts/debug_content.tscn delete mode 100644 src/ui_parts/display_texture.gd delete mode 100644 src/ui_parts/display_texture.gd.uid delete mode 100644 src/ui_parts/handles_manager.gd.uid create mode 100644 src/ui_parts/main_canvas.gd create mode 100644 src/ui_parts/main_canvas.gd.uid delete mode 100644 src/ui_parts/viewport.gd delete mode 100644 src/ui_parts/viewport.gd.uid delete mode 100644 src/ui_parts/viewport_container.gd delete mode 100644 src/ui_parts/viewport_container.gd.uid create mode 100644 src/ui_widgets/Canvas.gd create mode 100644 src/ui_widgets/Canvas.gd.uid rename src/{ui_parts => ui_widgets}/handles_manager.gd (90%) create mode 100644 src/ui_widgets/handles_manager.gd.uid create mode 100644 src/ui_widgets/viewport_controls.gd create mode 100644 src/ui_widgets/viewport_controls.gd.uid delete mode 100644 src/ui_widgets/zoom_menu.gd create mode 100644 src/ui_widgets/zoom_widget.gd rename src/ui_widgets/{zoom_menu.gd.uid => zoom_widget.gd.uid} (100%) rename src/ui_widgets/{zoom_menu.tscn => zoom_widget.tscn} (76%) diff --git a/assets/icons/backgrounds/Checkerboard.svg b/assets/icons/Checkerboard.svg similarity index 100% rename from assets/icons/backgrounds/Checkerboard.svg rename to assets/icons/Checkerboard.svg diff --git a/assets/icons/Checkerboard.svg.import b/assets/icons/Checkerboard.svg.import new file mode 100644 index 0000000..dc241b3 --- /dev/null +++ b/assets/icons/Checkerboard.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="SVGTexture" +uid="uid://c68og6bsqt0lb" +path="res://.godot/imported/Checkerboard.svg-6a6a77a30bc3d01a7e939a20ab5e8a17.svgtex" + +[deps] + +source_file="res://assets/icons/Checkerboard.svg" +dest_files=["res://.godot/imported/Checkerboard.svg-6a6a77a30bc3d01a7e939a20ab5e8a17.svgtex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/assets/icons/backgrounds/ColorButtonBG.svg b/assets/icons/CheckerboardColorButton.svg similarity index 100% rename from assets/icons/backgrounds/ColorButtonBG.svg rename to assets/icons/CheckerboardColorButton.svg diff --git a/assets/icons/CheckerboardColorButton.svg.import b/assets/icons/CheckerboardColorButton.svg.import new file mode 100644 index 0000000..7fa2a70 --- /dev/null +++ b/assets/icons/CheckerboardColorButton.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="SVGTexture" +uid="uid://y0l74x73w0co" +path="res://.godot/imported/CheckerboardColorButton.svg-a3714a8fc4a1322639155850c5bd0c5e.svgtex" + +[deps] + +source_file="res://assets/icons/CheckerboardColorButton.svg" +dest_files=["res://.godot/imported/CheckerboardColorButton.svg-a3714a8fc4a1322639155850c5bd0c5e.svgtex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/assets/icons/backgrounds/CheckerboardMini.svg b/assets/icons/CheckerboardMini.svg similarity index 100% rename from assets/icons/backgrounds/CheckerboardMini.svg rename to assets/icons/CheckerboardMini.svg diff --git a/assets/icons/CheckerboardMini.svg.import b/assets/icons/CheckerboardMini.svg.import new file mode 100644 index 0000000..4aa3006 --- /dev/null +++ b/assets/icons/CheckerboardMini.svg.import @@ -0,0 +1,18 @@ +[remap] + +importer="svg" +type="SVGTexture" +uid="uid://stpallv5q0rb" +path="res://.godot/imported/CheckerboardMini.svg-0ea7540124897b15442aca32bc10cdbd.svgtex" + +[deps] + +source_file="res://assets/icons/CheckerboardMini.svg" +dest_files=["res://.godot/imported/CheckerboardMini.svg-0ea7540124897b15442aca32bc10cdbd.svgtex"] + +[params] + +base_scale=1.0 +saturation=1.0 +color_map={} +compress=true diff --git a/assets/icons/backgrounds/Checkerboard.svg.import b/assets/icons/backgrounds/Checkerboard.svg.import deleted file mode 100644 index fa94a35..0000000 --- a/assets/icons/backgrounds/Checkerboard.svg.import +++ /dev/null @@ -1,18 +0,0 @@ -[remap] - -importer="svg" -type="SVGTexture" -uid="uid://c68og6bsqt0lb" -path="res://.godot/imported/Checkerboard.svg-f7e80961b46cfe0abb1fe7c57bd5c311.svgtex" - -[deps] - -source_file="res://assets/icons/backgrounds/Checkerboard.svg" -dest_files=["res://.godot/imported/Checkerboard.svg-f7e80961b46cfe0abb1fe7c57bd5c311.svgtex"] - -[params] - -base_scale=1.0 -saturation=1.0 -color_map={} -compress=true diff --git a/assets/icons/backgrounds/CheckerboardMini.svg.import b/assets/icons/backgrounds/CheckerboardMini.svg.import deleted file mode 100644 index 1bff5b5..0000000 --- a/assets/icons/backgrounds/CheckerboardMini.svg.import +++ /dev/null @@ -1,18 +0,0 @@ -[remap] - -importer="svg" -type="SVGTexture" -uid="uid://stpallv5q0rb" -path="res://.godot/imported/CheckerboardMini.svg-231a39c4ad6a476f0b2ee9407382b18a.svgtex" - -[deps] - -source_file="res://assets/icons/backgrounds/CheckerboardMini.svg" -dest_files=["res://.godot/imported/CheckerboardMini.svg-231a39c4ad6a476f0b2ee9407382b18a.svgtex"] - -[params] - -base_scale=1.0 -saturation=1.0 -color_map={} -compress=true diff --git a/assets/icons/backgrounds/ColorButtonBG.svg.import b/assets/icons/backgrounds/ColorButtonBG.svg.import deleted file mode 100644 index 28ea6d0..0000000 --- a/assets/icons/backgrounds/ColorButtonBG.svg.import +++ /dev/null @@ -1,18 +0,0 @@ -[remap] - -importer="svg" -type="SVGTexture" -uid="uid://y0l74x73w0co" -path="res://.godot/imported/ColorButtonBG.svg-f05e8d9d784151a74a26cfdebd155a7f.svgtex" - -[deps] - -source_file="res://assets/icons/backgrounds/ColorButtonBG.svg" -dest_files=["res://.godot/imported/ColorButtonBG.svg-f05e8d9d784151a74a26cfdebd155a7f.svgtex"] - -[params] - -base_scale=1.0 -saturation=1.0 -color_map={} -compress=true diff --git a/src/autoload/Configs.gd b/src/autoload/Configs.gd index 13324c2..be97c7c 100644 --- a/src/autoload/Configs.gd +++ b/src/autoload/Configs.gd @@ -1,4 +1,4 @@ -# This singleton handles session data and settings. +## An autoload that manages the savefile. Stores signals relating to changes to settings. extends Node @warning_ignore_start("unused_signal") @@ -14,7 +14,6 @@ signal selection_rectangle_visuals_changed signal grid_color_changed signal shortcut_panel_changed signal active_tab_status_changed -signal active_tab_reference_changed signal active_tab_changed signal tabs_changed signal tab_removed @@ -46,7 +45,9 @@ func check_orientation(): orientation_changed.emit() HandlerGUI.toogle_status_bar(new_orientation == orientation.PORTRAIT) -const savedata_path = "user://savedata.tres" +const _SAVEDATA_PATH = "user://savedata.tres" + +## Main point of access to the savefile data. This includes user settings and session data. var savedata: SaveData: set(new_value): if savedata != new_value and is_instance_valid(new_value): @@ -54,11 +55,12 @@ var savedata: SaveData: savedata.validate() savedata.changed_deferred.connect(save) - +## Helper for updating the savefile. func save() -> void: - ResourceSaver.save(savedata, savedata_path) + ResourceSaver.save(savedata, _SAVEDATA_PATH) +## Default shortcuts to be able to reset a single action to its defaults. var default_shortcuts: Dictionary[String, Array] = {} func _enter_tree() -> void: @@ -66,32 +68,26 @@ func _enter_tree() -> void: for action in ShortcutUtils.get_all_actions(): if InputMap.has_action(action): default_shortcuts[action] = InputMap.action_get_events(action) - load_config() - - -func load_config() -> void: - if not FileAccess.file_exists(savedata_path): - reset_settings() - return - savedata = ResourceLoader.load(savedata_path) - if not is_instance_valid(savedata): - reset_settings() - return + var savedata_reset_needed := true + # Load savedata. + if FileAccess.file_exists(_SAVEDATA_PATH): + savedata = ResourceLoader.load(_SAVEDATA_PATH) + if is_instance_valid(savedata): + savedata_reset_needed = false - post_load() - - -func reset_settings() -> void: - savedata = SaveData.new() - savedata.reset_to_default() - savedata.language = "en" - savedata.set_shortcut_panel_slots({ 0: "ui_undo", 1: "ui_redo", 2: "duplicate", 3: "delete", 4: "import", 5: "export"}) - savedata.set_palettes([Palette.new("Pure", Palette.Preset.PURE)]) - save() - post_load() - -func post_load() -> void: + # Build everything from scratch if the savedata didn't exist or was invalid. + # Reset settings to their defaults. + if savedata_reset_needed: + savedata = SaveData.new() + savedata.reset_to_default() + savedata.language = "en" + savedata.set_shortcut_panel_slots({ 0: "ui_undo", 1: "ui_redo", 2: "duplicate", 3: "delete", 4: "import", 5: "export"}) + savedata.set_palettes([Palette.new("Pure", Palette.Preset.PURE)]) + save() + + # Syncs various settings with their savedata value. + # TODO I'm not sure why I made it so syncing within the SaveData only starts when it's fully initialized. savedata.get_active_tab().activate() sync_canvas_color() sync_locale() @@ -100,27 +96,30 @@ func post_load() -> void: sync_theme() -# Global effects from settings. Some of them should also be used on launch. - +## Syncs the canvas color to the value in the savedata. func sync_canvas_color() -> void: RenderingServer.set_default_clear_color(savedata.canvas_color) +## Syncs the locale to the value in the savedata. func sync_locale() -> void: if not savedata.language in TranslationServer.get_loaded_locales(): savedata.language = "en" else: TranslationServer.set_locale(savedata.language) +## Syncs the VSync to the value in the savedata. func sync_vsync() -> void: - DisplayServer.window_set_vsync_mode( - DisplayServer.VSYNC_ENABLED if savedata.vsync else DisplayServer.VSYNC_DISABLED) + DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if savedata.vsync else DisplayServer.VSYNC_DISABLED) +## Syncs the engine max FPS to the value in the savedata. func sync_max_fps() -> void: Engine.max_fps = savedata.max_fps +## Syncs the display server's "Keep screen on" behavior to the value in the savedata. func sync_keep_screen_on() -> void: DisplayServer.screen_set_keep_on(savedata.keep_screen_on) +## Syncs the app theme based on the configurations in the savedata. func sync_theme() -> void: ThemeUtils.generate_and_apply_theme() theme_changed.emit() diff --git a/src/autoload/HandlerGUI.gd b/src/autoload/HandlerGUI.gd index f044eef..3c8c97c 100644 --- a/src/autoload/HandlerGUI.gd +++ b/src/autoload/HandlerGUI.gd @@ -1,3 +1,4 @@ +## An autoload that handles various core UI functions. extends Node const AlertDialogScene = preload("res://src/ui_widgets/alert_dialog.tscn") @@ -10,9 +11,16 @@ const ExportMenuScene = preload("res://src/ui_parts/export_menu.tscn") const ShortcutPanelScene = preload("res://src/ui_parts/shortcut_panel.tscn") const TabsPanel = preload("res://src/ui_parts/tabs_panel.tscn") -# Menus should be added with add_menu() and removed by being freed. -# To add them as modals that don't hide the previous one, use add_dialog(). +signal popups_cleared + +## A stack of the current menus, dialogs in the foreground, center of the screen, that also darken the background for more focus. +## Menus should be added with add_menu(), which hides previous menus, or add_dialog() which doesn't hide previous menus. +## Menus are removed by being freed, which automatically removes them from the stack. +## This is typically done by tying a cancel button to queue_free, or pressing Esc. var menu_stack: Array[ColorRect] + +## A stack of the current popups. They are cleared by pressing Esc, clicking outside, or other ways. +## Popups are always on top of the menu stack, and changes to the menu stack clear all popups. var popup_stack: Array[Control] var shortcut_panel: PanelContainer @@ -36,6 +44,10 @@ func toogle_status_bar(visible: bool, override := false) -> void: VTAndroidCore.toogleStatusBar(visible) status_bar_visible = visible +## A dictionary for shortcut registrations for each node. +## If a node is freed, its shortcut registrations are cleared. +## Every time a shortcut is pressed, the list of registrations is walked through to find if there's an appropriate one. +## For a registratition to be appropriate, its behavior must coincide with where the node is relative to the menu/popup stack. var shortcut_registrations: Dictionary[Node, ShortcutsRegistration] = {} func _enter_tree() -> void: @@ -53,7 +65,7 @@ func _enter_tree() -> void: shortcuts.add_shortcut("open_in_folder", func() -> void: FileUtils.open_svg_folder(Configs.savedata.get_active_tab().svg_file_path), ShortcutsRegistration.Behavior.PASS_THROUGH_ALL) register_shortcuts(self, shortcuts) - + # Connect window signals to appropriate methods. var window := get_window() window.files_dropped.connect(_on_files_dropped) window.dpi_changed.connect(update_ui_scale) @@ -90,28 +102,30 @@ func _notification(what: int) -> void: if not status_bar_visible: toogle_status_bar(false, true) -# Drag-and-drop of files. +# Handles drag-and-drop of files. func _on_files_dropped(files: PackedStringArray) -> void: if menu_stack.is_empty(): get_window().grab_focus() FileUtils.apply_svgs_from_paths(files) - +## Registers the given set of shortcuts to the given node. func register_shortcuts(node: Node, registrations: ShortcutsRegistration) -> void: shortcut_registrations[node] = registrations node.tree_exiting.connect(func() -> void: shortcut_registrations.erase(node)) - +## Adds a new menu to menu_stack which hides the previous one. func add_menu(new_menu: Control) -> void: if not menu_stack.is_empty(): menu_stack.back().hide() _add_control(new_menu) +## Adds a new menu to the menu_stack that is overlaid on top of the previous one. func add_dialog(new_dialog: Control) -> void: if not menu_stack.is_empty(): menu_stack.back().show() _add_control(new_dialog) +# Common logic for add_menu() and add_dialog(). func _add_control(new_control: Control) -> void: # FIXME subpar workaround to drag & drop not able to be canceled manually. get_tree().root.propagate_notification(NOTIFICATION_DRAG_END) @@ -147,13 +161,12 @@ func _remove_control(overlay_ref: ColorRect = null) -> void: overlay_ref.queue_free() throw_mouse_motion_event() +## Frees all nodes in the menu_stack, emptying it. func remove_all_menus() -> void: - if menu_stack.is_empty(): - return - - while not menu_stack.is_empty(): - menu_stack.pop_back().queue_free() - throw_mouse_motion_event() + if not menu_stack.is_empty(): + while not menu_stack.is_empty(): + menu_stack.pop_back().queue_free() + throw_mouse_motion_event() # The passed popup control may be added to a shadow panel. The shadow panel is @@ -201,15 +214,17 @@ func remove_popup(overlay_ref: Control = null) -> void: overlay_ref = popup_stack.pop_back() if is_instance_valid(overlay_ref): overlay_ref.queue_free() + + if popup_stack.is_empty(): + popups_cleared.emit() throw_mouse_motion_event() func remove_all_popups() -> void: - if popup_stack.is_empty(): - return - - while not popup_stack.is_empty(): - popup_stack.pop_back().queue_free() - throw_mouse_motion_event() + if not popup_stack.is_empty(): + while not popup_stack.is_empty(): + popup_stack.pop_back().queue_free() + popups_cleared.emit() + throw_mouse_motion_event() # Should usually be the global rect of a control. @@ -259,8 +274,7 @@ func popup_clamp_pos(popup: Control, attempt_pos: Vector2, vp: Viewport) -> Vect func _parse_popup_overlay_event(event: InputEvent) -> void: if not popup_stack.is_empty(): - if event is InputEventMouseButton and event.button_index in [MOUSE_BUTTON_LEFT, - MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_RIGHT]: + if event is InputEventMouseButton and event.button_index in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_MIDDLE, MOUSE_BUTTON_RIGHT]: remove_popup() get_viewport().set_input_as_handled() @@ -332,8 +346,7 @@ func _unhandled_input(event: InputEvent) -> void: func get_window_default_size() -> Vector2i: - return Vector2i(ProjectSettings.get_setting("display/window/size/viewport_width"), - ProjectSettings.get_setting("display/window/size/viewport_height")) + return Vector2i(ProjectSettings.get_setting("display/window/size/viewport_width"), ProjectSettings.get_setting("display/window/size/viewport_height")) func get_usable_rect() -> Vector2i: var window := get_window() @@ -400,8 +413,7 @@ func toggle_fullscreen() -> void: was_window_maximized = true else: was_window_maximized = false - window_old_rect = Rect2(DisplayServer.window_get_position(), - DisplayServer.window_get_size()) + window_old_rect = Rect2(DisplayServer.window_get_position(), DisplayServer.window_get_size()) DisplayServer.window_set_mode(DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN) else: if was_window_maximized: @@ -456,11 +468,9 @@ func open_export() -> void: var message: String if dimensions_too_different: - message = Translator.translate( - "The graphic can be exported only as SVG because its proportions are too extreme.") + message = Translator.translate("The graphic can be exported only as SVG because its proportions are too extreme.") else: - message = Translator.translate( - "The graphic can be exported only as SVG because its size is not defined.") + message = Translator.translate("The graphic can be exported only as SVG because its size is not defined.") message += "\n\n" + Translator.translate("Do you want to proceed?") var confirm_dialog := ConfirmDialogScene.instantiate() @@ -478,8 +488,7 @@ func update_window_title() -> void: # Helpers -# Used to trigger a mouse motion event, which can be used to update some things, -# when Godot doesn't want to do so automatically. +## Triggers a mouse motion event, which can update hover or mouse shape if Godot didn't do it automatically. func throw_mouse_motion_event() -> void: var mm_event := InputEventMouseMotion.new() var window := get_window() @@ -494,7 +503,7 @@ func throw_mouse_motion_event() -> void: mm_event.position = mouse_position * window.get_final_transform() Input.parse_input_event.call_deferred(mm_event) -# Trigger a shortcut automatically. +## Triggers a shortcut. func throw_action_event(action: String) -> void: var events := InputMap.action_get_events(action) for event in events: diff --git a/src/autoload/State.gd b/src/autoload/State.gd index 1e7b81e..e9baeab 100644 --- a/src/autoload/State.gd +++ b/src/autoload/State.gd @@ -1,11 +1,10 @@ -# This singleton handles information that's session-wide, but not saved. +## An autoload that handles information that's session-wide, but not saved. extends Node const OptionsDialogScene = preload("res://src/ui_widgets/options_dialog.tscn") const PathCommandPopupScene = preload("res://src/ui_widgets/path_popup.tscn") signal svg_unknown_change -signal svg_resized # These signals copy the ones in ElementRoot. # ElementRoot is not persistent, while these signals can be connected to reliably. @@ -21,8 +20,6 @@ signal basic_xnode_rendered_text_changed signal parsing_finished(error_id: SVGParser.ParseError) signal svg_changed # Should only connect to persistent parts of the UI. -var _svg_current_size := Vector2.ZERO - var _update_pending := false # "unstable_text" is the current state, which might have errors (i.e., while using the @@ -111,7 +108,7 @@ func _update() -> void: if not _update_pending: return _update_pending = false - svg_text = SVGParser.root_to_editor_text(root_element) + svg_text = SVGParser.root_to_editor_markup(root_element) svg_changed.emit() @@ -133,7 +130,7 @@ func sync_to_editor_formatter() -> void: func sync_elements() -> void: var text_to_parse := svg_text if unstable_svg_text.is_empty() else unstable_svg_text - var svg_parse_result := SVGParser.text_to_root(text_to_parse) + var svg_parse_result := SVGParser.markup_to_root(text_to_parse) parsing_finished.emit(svg_parse_result.error) if svg_parse_result.error == SVGParser.ParseError.OK: svg_text = unstable_svg_text @@ -145,22 +142,9 @@ func sync_elements() -> void: root_element.xnodes_moved_in_parent.connect(xnodes_moved_in_parent.emit) root_element.xnodes_moved_to.connect(xnodes_moved_to.emit) root_element.xnode_layout_changed.connect(xnode_layout_changed.emit) - root_element.attribute_changed.connect(_on_root_attribute_changed) root_element.basic_xnode_text_changed.connect(basic_xnode_text_changed.emit) - root_element.basic_xnode_rendered_text_changed.connect( - basic_xnode_rendered_text_changed.emit) + root_element.basic_xnode_rendered_text_changed.connect(basic_xnode_rendered_text_changed.emit) svg_unknown_change.emit() - _update_svg_current_size() - - -func _on_root_attribute_changed(attribute_name: String) -> void: - if attribute_name in ["width", "height", "viewBox"]: - _update_svg_current_size() - -func _update_svg_current_size() -> void: - if _svg_current_size != root_element.get_size(): - _svg_current_size = root_element.get_size() - svg_resized.emit() func apply_svg_text(new_text: String, save := true) -> void: @@ -174,7 +158,7 @@ func optimize() -> void: queue_svg_save() func get_export_text() -> String: - return SVGParser.root_to_export_text(root_element) + return SVGParser.root_to_export_markup(root_element) signal hover_changed @@ -225,64 +209,6 @@ func clear_proposed_drop_xid() -> void: proposed_drop_changed.emit() -signal zoom_changed -@warning_ignore("unused_signal") -signal view_changed -signal viewport_size_changed - -var zoom := 0.0 -var viewport_size := Vector2i.ZERO - -func set_zoom(new_value: float) -> void: - if zoom != new_value: - zoom = new_value - zoom_changed.emit() - -func set_viewport_size(new_value: Vector2i) -> void: - if viewport_size != new_value: - viewport_size = new_value - viewport_size_changed.emit() - - -var view_rasterized := false -var show_grid := true -var show_handles := true -var show_reference := true -var overlay_reference := false -var show_debug := false - -signal view_rasterized_changed -signal show_grid_changed -signal show_handles_changed -signal show_reference_changed -signal overlay_reference_changed -signal show_debug_changed - -func toggle_view_rasterized() -> void: - view_rasterized = not view_rasterized - view_rasterized_changed.emit() - -func toggle_show_grid() -> void: - show_grid = not show_grid - show_grid_changed.emit() - -func toggle_show_handles() -> void: - show_handles = not show_handles - show_handles_changed.emit() - -func toggle_show_reference() -> void: - show_reference = not show_reference - show_reference_changed.emit() - -func toggle_overlay_reference() -> void: - overlay_reference = not overlay_reference - overlay_reference_changed.emit() - -func toggle_show_debug() -> void: - show_debug = not show_debug - show_debug_changed.emit() - - # Override the selected elements with a single new selected element. # If inner_idx is given, this will be an inner selection. func normal_select(xid: PackedInt32Array, inner_idx := -1) -> void: @@ -506,34 +432,27 @@ func clear_all_hovered() -> void: # Returns whether the given element or inner editor is hovered. func is_hovered(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> bool: if propagate: + if XIDUtils.is_ancestor_or_self(hovered_xid, xid): + return true if inner_idx == -1: - return XIDUtils.is_parent_or_self(hovered_xid, xid) - else: - return (inner_hovered == inner_idx and semi_hovered_xid == xid) or XIDUtils.is_parent_or_self(hovered_xid, xid) - else: - if inner_idx == -1: - return hovered_xid == xid - else: - return inner_hovered == inner_idx and semi_hovered_xid == xid + return false + return inner_hovered == inner_idx and semi_hovered_xid == xid + if inner_idx == -1: + return hovered_xid == xid + return inner_hovered == inner_idx and semi_hovered_xid == xid # Returns whether the given element or inner editor is selected. func is_selected(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> bool: if propagate: + for selected_xid in selected_xids: + if XIDUtils.is_ancestor_or_self(selected_xid, xid): + return true if inner_idx == -1: - for selected_xid in selected_xids: - if XIDUtils.is_parent_or_self(selected_xid, xid): - return true return false - else: - for selected_xid in selected_xids: - if XIDUtils.is_parent_or_self(selected_xid, xid): - return true - return semi_selected_xid == xid and inner_idx in inner_selections - else: - if inner_idx == -1: - return xid in selected_xids - else: - return semi_selected_xid == xid and inner_idx in inner_selections + return semi_selected_xid == xid and inner_idx in inner_selections + if inner_idx == -1: + return xid in selected_xids + return semi_selected_xid == xid and inner_idx in inner_selections # Returns whether the selection matches a subpath. func is_selection_subpath() -> bool: @@ -554,6 +473,7 @@ func is_selection_subpath() -> bool: func _on_xnodes_added(xids: Array[PackedInt32Array]) -> void: selected_xids = xids.duplicate() selection_pivot_xid = xids[-1] + selection_changed.emit() # If selected elements were deleted, remove them from the list of selected elements. func _on_xnodes_deleted(xids: Array[PackedInt32Array]) -> void: @@ -562,7 +482,7 @@ func _on_xnodes_deleted(xids: Array[PackedInt32Array]) -> void: for deleted_xid in xids: for i in range(selected_xids.size() - 1, -1, -1): var xid := selected_xids[i] - if XIDUtils.is_parent_or_self(deleted_xid, xid): + if XIDUtils.is_ancestor_or_self(deleted_xid, xid): selected_xids.remove_at(i) if not XIDUtils.are_xid_lists_same(old_selected_xids, selected_xids): selection_changed.emit() @@ -583,7 +503,7 @@ func _on_xnodes_moved_in_parent(parent_xid: PackedInt32Array, indices: Array[int # If the XID or a child of it is found, append it. for xid in selected_xids: - if XIDUtils.is_parent_or_self(old_moved_xid, xid): + if XIDUtils.is_ancestor_or_self(old_moved_xid, xid): var new_selected_xid := xid.duplicate() new_selected_xid[parent_xid.size()] = index_idx xids_to_unselect.append(xid) @@ -602,7 +522,7 @@ func _on_xnodes_moved_to(xids: Array[PackedInt32Array], location: PackedInt32Arr for moved_idx in xids.size(): var moved_xid := xids[moved_idx] for xid in selected_xids: - if XIDUtils.is_parent_or_self(moved_xid, xid): + if XIDUtils.is_ancestor_or_self(moved_xid, xid): var new_location := XIDUtils.get_parent_xid(location) new_location.append(moved_idx + location[-1]) for ii in range(moved_xid.size(), xid.size()): @@ -789,9 +709,8 @@ func get_selection_context(popup_method: Callable, context: Utils.LayoutPart) -> # , "Move Subpath Down" "polygon", "polyline": if inner_selections.size() == 1: - btn_arr.append(ContextPopup.create_button( - Translator.translate("Insert After"), insert_point_after_selection, - false, load("res://assets/icons/Plus.svg"))) + btn_arr.append(ContextPopup.create_button(Translator.translate("Insert After"), + insert_point_after_selection, false, load("res://assets/icons/Plus.svg"))) btn_arr.append(ContextPopup.create_shortcut_button("delete")) @@ -807,23 +726,21 @@ func popup_convert_to_context(popup_method: Callable) -> void: if not xnode.is_element(): for xnode_type in xnode.get_possible_conversions(): var btn := ContextPopup.create_button(BasicXNode.get_type_string(xnode_type), - convert_selected_xnode_to.bind(xnode_type), - false, DB.get_xnode_icon(xnode_type)) + convert_selected_xnode_to.bind(xnode_type), false, DB.get_xnode_icon(xnode_type)) btn.add_theme_font_override("font", ThemeUtils.mono_font) btn_arr.append(btn) else: for element_name in xnode.possible_conversions: var btn := ContextPopup.create_button(element_name, convert_selected_element_to.bind(element_name), - !xnode.can_replace(element_name), DB.get_element_icon(element_name)) + not xnode.can_replace(element_name), DB.get_element_icon(element_name)) btn.add_theme_font_override("font", ThemeUtils.mono_font) btn_arr.append(btn) var context_popup := ContextPopup.new() context_popup.setup(btn_arr, true) popup_method.call(context_popup) elif not inner_selections.is_empty() and not semi_selected_xid.is_empty(): - var path_attrib: AttributePathdata = root_element.get_xnode( - semi_selected_xid).get_attribute("d") + var path_attrib: AttributePathdata = root_element.get_xnode(semi_selected_xid).get_attribute("d") var selection_idx: int = inner_selections.max() var cmd_char := path_attrib.get_command(selection_idx).command_char @@ -845,8 +762,7 @@ func popup_convert_to_context(popup_method: Callable) -> void: command_picker.path_command_picked.connect(convert_selected_command_to) func popup_insert_command_after_context(popup_method: Callable) -> void: - var path_attrib: AttributePathdata = root_element.get_xnode( - semi_selected_xid).get_attribute("d") + var path_attrib: AttributePathdata = root_element.get_xnode(semi_selected_xid).get_attribute("d") var selection_idx: int = inner_selections.max() var cmd_char := path_attrib.get_command(selection_idx).command_char @@ -856,10 +772,8 @@ func popup_insert_command_after_context(popup_method: Callable) -> void: # Disable invalid commands. Z is syntactically invalid, so disallow it even harder. var warned_commands: PackedStringArray var disabled_commands: PackedStringArray - # S commands are deliberately warned against in most cases, even though - # there is some sense in using them without a C or S command before them. - # Same for T commands in most cases, even though - # there is a notion of letting them determine the next shorthand quadratic curve. + # S commands are deliberately warned against in most cases, even though there is some sense in using them without a C or S command before them. + # Same for T commands in most cases, even though there is a notion of letting them determine the next shorthand quadratic curve. match cmd_char.to_upper(): "M": warned_commands = PackedStringArray(["M", "Z", "S", "T"]) "L", "H", "V", "A": warned_commands = PackedStringArray(["S", "T"]) @@ -873,17 +787,14 @@ func popup_insert_command_after_context(popup_method: Callable) -> void: func convert_selected_element_to(element_name: String) -> void: var xid := selected_xids[0] - root_element.replace_xnode(xid, - root_element.get_xnode(xid).get_replacement(element_name)) + root_element.replace_xnode(xid, root_element.get_xnode(xid).get_replacement(element_name)) queue_svg_save() func convert_selected_xnode_to(xnode_type: BasicXNode.NodeType) -> void: var xid := selected_xids[0] - root_element.replace_xnode(xid, - root_element.get_xnode(xid).get_replacement(xnode_type)) + root_element.replace_xnode(xid, root_element.get_xnode(xid).get_replacement(xnode_type)) queue_svg_save() func convert_selected_command_to(cmd_type: String) -> void: - root_element.get_xnode(semi_selected_xid).get_attribute("d").convert_command( - inner_selections[0], cmd_type) + root_element.get_xnode(semi_selected_xid).get_attribute("d").convert_command(inner_selections[0], cmd_type) queue_svg_save() diff --git a/src/config_classes/ConfigResource.gd b/src/config_classes/ConfigResource.gd index 2e2d5e4..0c3180f 100644 --- a/src/config_classes/ConfigResource.gd +++ b/src/config_classes/ConfigResource.gd @@ -1,6 +1,7 @@ -# Implements a very useful signal. +## A resource that implements the very useful changed_deferred signal. @abstract class_name ConfigResource extends Resource +## Emitted together with changed signals, but only once per frame. signal changed_deferred var _initial_load_finished := false @@ -12,14 +13,15 @@ func _init() -> void: func _queue_emit_changed_deferred() -> void: if _initial_load_finished: - emit_changed_deferred.call_deferred() + _emit_changed_deferred.call_deferred() _changed_deferred_pending = true -func emit_changed_deferred() -> void: +func _emit_changed_deferred() -> void: if _changed_deferred_pending: _changed_deferred_pending = false changed_deferred.emit() +## Calls an external method, generally a global one that syncs things to a new resource value. func external_call(callback: Callable) -> void: if _initial_load_finished: callback.call_deferred() diff --git a/src/config_classes/Formatter.gd b/src/config_classes/Formatter.gd index a7bfb17..b786a5b 100644 --- a/src/config_classes/Formatter.gd +++ b/src/config_classes/Formatter.gd @@ -1,4 +1,5 @@ -# A resource used to determine how to structure the XML and represent attributes. +## A resource used to determine how to structure the XML and format attribute values. +## The various configs are described interactively in the GodSVG settings menu. class_name Formatter extends ConfigResource enum Preset {COMPACT, PRETTY} @@ -7,7 +8,7 @@ enum NamedColorUse {ALWAYS, WHEN_SHORTER_OR_EQUAL, WHEN_SHORTER, NEVER} enum PrimaryColorSyntax {THREE_OR_SIX_DIGIT_HEX, SIX_DIGIT_HEX, RGB} # Elements that don't make sense without child elements. -const container_elements: Array[String] = ["svg", "g", "linearGradient", "radialGradient"] +const CONTAINER_ELEMENTS: PackedStringArray = ["svg", "g", "linearGradient", "radialGradient"] # TODO Typed Dictionary wonkiness Dictionary[Preset, String] static func get_preset_value_text_map() -> Dictionary: @@ -50,7 +51,7 @@ static func get_enum_value_text_map(property: String) -> Dictionary: "color_primary_syntax": return get_primary_color_syntax_value_text_map() return {} - +## Returns the default of a settings based on the preset. func get_setting_default(setting: String) -> Variant: match setting: "xml_add_trailing_newline": return false @@ -75,10 +76,12 @@ func get_setting_default(setting: String) -> Variant: "transform_list_remove_unnecessary_params": return preset == Preset.COMPACT return null +## Resets all settings to their defaults based on the preset. func reset_to_default() -> void: for setting in _get_setting_names(): set(setting, get_setting_default(setting)) +## Returns true if all settings are set to their default value based on the preset. func is_everything_default() -> bool: for setting in _get_setting_names(): if get(setting) != get_setting_default(setting): @@ -99,6 +102,7 @@ func _init(new_preset := Preset.COMPACT) -> void: super() +## A preset that determines the default values of the formatter configs. @export var preset := Preset.COMPACT: set(new_value): if preset != new_value: diff --git a/src/config_classes/Palette.gd b/src/config_classes/Palette.gd index b2eb430..b6fefc8 100644 --- a/src/config_classes/Palette.gd +++ b/src/config_classes/Palette.gd @@ -1,17 +1,14 @@ -# A resource for the color palettes that are listed in the color picker. +## A resource for the color palettes that are listed in the color picker. class_name Palette extends ConfigResource enum Preset {EMPTY, PURE, GRAYSCALE} var _presets: Dictionary[Preset, Array] = { Preset.EMPTY: [PackedStringArray(), PackedStringArray()], - Preset.PURE: [PackedStringArray(["#fff", "#000", "#f00", "#0f0", "#00f", "#ff0", - "#f0f", "#0ff"]), PackedStringArray(["White", "Black", "Red", "Green", "Blue", - "Yellow", "Magenta", "Cyan"])], - Preset.GRAYSCALE: [PackedStringArray(["#000", "#1a1a1a", "#333", "#4d4d4d", "#666", - "#808080", "#999", "#b3b3b3", "#ccc", "#e6e6e6", "#fff"]), PackedStringArray([ - "Black", "10% Gray", "20% Gray", "30% Gray", "40% Gray", "50% Gray", "60% Gray", - "70% Gray", "80% Gray", "90% Gray", "White"])], + Preset.PURE: [PackedStringArray(["#fff", "#000", "#f00", "#0f0", "#00f", "#ff0", "#f0f", "#0ff"]), + PackedStringArray(["White", "Black", "Red", "Green", "Blue", "Yellow", "Magenta", "Cyan"])], + Preset.GRAYSCALE: [PackedStringArray(["#000", "#1a1a1a", "#333", "#4d4d4d", "#666", "#808080", "#999", "#b3b3b3", "#ccc", "#e6e6e6", "#fff"]), + PackedStringArray(["Black", "10% Gray", "20% Gray", "30% Gray", "40% Gray", "50% Gray", "60% Gray", "70% Gray", "80% Gray", "90% Gray", "White"])], } signal layout_changed @@ -129,8 +126,8 @@ func has_unique_definitions() -> bool: dict[color] = [_color_names[i]] return true - -func to_text() -> String: +## Returns the palette converted into equivalent markup. +func get_as_markup() -> String: var text := '\n' % title for i in _colors.size(): text += '\t Dictionary: # Helpers. func get_element_color(element_name: String) -> Color: - return element_color if element_name in DB.recognized_elements else unrecognized_element_color + return element_color if DB.is_element_recognized(element_name) else unrecognized_element_color func get_attribute_color(element_name: String, attribute_name: String) -> Color: return attribute_color if DB.is_attribute_recognized(element_name, attribute_name) else unrecognized_attribute_color diff --git a/src/config_classes/SaveData.gd b/src/config_classes/SaveData.gd index 9e13341..08919fd 100644 --- a/src/config_classes/SaveData.gd +++ b/src/config_classes/SaveData.gd @@ -60,15 +60,15 @@ func get_setting_default(setting: String) -> Variant: HighlighterPreset.DEFAULT_LIGHT: return Color("cc0000") "basic_color_valid": match theme_preset: - ThemePreset.DARK,ThemePreset.BLACK: return Color("9f9") + ThemePreset.DARK, ThemePreset.BLACK: return Color("9f9") ThemePreset.LIGHT: return Color("2b2") "basic_color_error": match theme_preset: - ThemePreset.DARK,ThemePreset.BLACK: return Color("f99") + ThemePreset.DARK, ThemePreset.BLACK: return Color("f99") ThemePreset.LIGHT: return Color("b22") "basic_color_warning": match theme_preset: - ThemePreset.DARK,ThemePreset.BLACK: return Color("ee6") + ThemePreset.DARK, ThemePreset.BLACK: return Color("ee6") ThemePreset.LIGHT: return Color("991") "handle_size": return 1.0 if OS.get_name() != "Android" else 2.0 "handle_inner_color": return Color("fff") @@ -88,7 +88,7 @@ func get_setting_default(setting: String) -> Variant: ThemePreset.BLACK: return Color("000") "grid_color": match theme_preset: - ThemePreset.DARK,ThemePreset.BLACK: return Color("808080") + ThemePreset.DARK, ThemePreset.BLACK: return Color("808080") ThemePreset.LIGHT: return Color("666") "invert_zoom": return false @@ -102,19 +102,22 @@ func get_setting_default(setting: String) -> Variant: "use_filename_for_window_title": return true return null +## Resets all settings to their defaults. func reset_to_default() -> void: for setting in _get_setting_names(): set(setting, get_setting_default(setting)) +## Resets the settings tied to theme presets to their defaults, based on the current preset. func reset_theme_items_to_default() -> void: var old_highlighter_preset_value := highlighter_preset - for setting in theme_items: + for setting in THEME_ITEMS: set(setting, get_setting_default(setting)) if old_highlighter_preset_value != highlighter_preset: reset_highlighting_items_to_default() +## Resets the settings tied to highlighter presets to their defaults, based on the current preset. func reset_highlighting_items_to_default() -> void: - for setting in highlighting_items: + for setting in HIGHLIGHTING_ITEMS: set(setting, get_setting_default(setting)) func _get_setting_names() -> PackedStringArray: @@ -125,7 +128,7 @@ func _get_setting_names() -> PackedStringArray: arr.append(p.name) return arr -const theme_items: PackedStringArray = [ +const THEME_ITEMS: PackedStringArray = [ "base_color", "accent_color", "highlighter_preset", @@ -137,12 +140,12 @@ const theme_items: PackedStringArray = [ ] func is_theming_default() -> bool: - for setting in theme_items: + for setting in THEME_ITEMS: if get(setting) != get_setting_default(setting): return false return true -# TODO Typed Dictionary wonkiness Dictionary[ThemePreset, String]. This one was copied +# TODO Typed Dictionary wonkiness Dictionary[ThemePreset, String]. This one was copied # from an earlier similar implementation, but I didn't bother to test if it's still # necessary because VectorTouch was disheveled while I was implementing the feature. static func get_theme_preset_value_text_map() -> Dictionary: @@ -152,7 +155,7 @@ static func get_theme_preset_value_text_map() -> Dictionary: ThemePreset.BLACK: Translator.translate("Black (OLED)"), } -const highlighting_items: PackedStringArray = [ +const HIGHLIGHTING_ITEMS: PackedStringArray = [ "highlighting_symbol_color", "highlighting_element_color", "highlighting_attribute_color", @@ -164,7 +167,7 @@ const highlighting_items: PackedStringArray = [ ] func is_highlighting_default() -> bool: - for setting in highlighting_items: + for setting in HIGHLIGHTING_ITEMS: if get(setting) != get_setting_default(setting): return false return true @@ -856,7 +859,6 @@ const MAX_TABS = 4096 for tab in _tabs: tab.changed.connect(emit_changed) tab.status_changed.connect(_on_tab_status_changed.bind(tab.id)) - tab.reference_changed.connect(_on_tab_reference_changed.bind(tab.id)) emit_changed() if _tabs.is_empty(): _add_new_tab() @@ -881,10 +883,6 @@ func _on_tab_status_changed(id: int) -> void: Configs.active_tab_status_changed.emit() Configs.tabs_changed.emit() -func _on_tab_reference_changed(id: int) -> void: - if id == _tabs[_active_tab_index].id: - Configs.active_tab_reference_changed.emit() - func has_tabs() -> bool: return not _tabs.is_empty() @@ -937,7 +935,6 @@ func _add_new_tab() -> void: new_tab.fully_loaded = false new_tab.changed.connect(emit_changed) new_tab.status_changed.connect(_on_tab_status_changed.bind(new_id)) - new_tab.reference_changed.connect(_on_tab_reference_changed.bind(new_id)) # Clear file path for the new tab. var new_tab_path := new_tab.get_edited_file_path() diff --git a/src/config_classes/TabData.gd b/src/config_classes/TabData.gd index 1d33c28..88545cf 100644 --- a/src/config_classes/TabData.gd +++ b/src/config_classes/TabData.gd @@ -7,7 +7,6 @@ const DEFAULT_SVG = ' void: if active: FileAccess.open(edited_file_path, FileAccess.WRITE).store_string(_svg_text) else: - var edited_text_parse_result := SVGParser.text_to_root( - FileAccess.get_file_as_string(get_edited_file_path())) + var edited_text_parse_result := SVGParser.markup_to_root(FileAccess.get_file_as_string(get_edited_file_path())) if is_instance_valid(edited_text_parse_result.svg): - FileAccess.open(edited_file_path, FileAccess.WRITE).store_string( - SVGParser.root_to_export_text(edited_text_parse_result.svg)) + FileAccess.open(edited_file_path, FileAccess.WRITE).store_string(SVGParser.root_to_export_markup(edited_text_parse_result.svg)) queue_sync() func save_to_bound_path() -> void: @@ -161,16 +156,16 @@ func _sync() -> void: if not fully_loaded: marked_unsaved = false else: - var edited_text_parse_result := SVGParser.text_to_root( + var edited_text_parse_result := SVGParser.markup_to_root( FileAccess.get_file_as_string(get_edited_file_path())) if is_instance_valid(edited_text_parse_result.svg): - marked_unsaved = FileAccess.get_file_as_string(svg_file_path) != SVGParser.root_to_export_text(edited_text_parse_result.svg) + marked_unsaved = FileAccess.get_file_as_string(svg_file_path) != SVGParser.root_to_export_markup(edited_text_parse_result.svg) else: marked_unsaved = true - elif not FileAccess.file_exists(get_edited_file_path()) or SVGParser.text_check_is_root_empty(get_true_svg_text()): + elif not FileAccess.file_exists(get_edited_file_path()) or SVGParser.markup_check_is_root_empty(get_true_svg_text()): empty_unsaved = true marked_unsaved = false presented_name = "[ %s ]" % Translator.translate("Empty") diff --git a/src/data_classes/Attribute.gd b/src/data_classes/Attribute.gd index 1813540..b5bcf56 100644 --- a/src/data_classes/Attribute.gd +++ b/src/data_classes/Attribute.gd @@ -1,6 +1,6 @@ -# Represents an attribute inside an element, i.e. . -# If the Attribute's data type is known, one of the inheriting classes should be used. -# By default uses the editor formatter. +## Represents an attribute inside an element, i.e. . +## If the Attribute's data type is known, one of the inheriting classes should be used. +## By default uses the editor formatter. class_name Attribute signal value_changed @@ -35,20 +35,23 @@ 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: + +## Returns a NameValidityLevel value based on whether the id is valid. +## If it's not a valid nametoken, it should be treated as a warning. +## Nmtoken syntax source: https://www.w3.org/TR/REC-xml/#NT-Nmtoken +static func get_id_validity(id: String) -> NameValidityLevel: var validity_level := NameValidityLevel.VALID for id_char in id: if id_char in ":_-.": continue var u := ord(id_char) - 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\ + if (u >= 48 and u <= 57) or (u >= 65 and u <= 90) or (u >= 97 and u <= 122) or u == 0xB7 or (u >= 0xC0 and u <= 0xD6) or (u >= 0xD8 and u <= 0xF6) or\ + (u >= 0xF8 and u <= 0x37D) or (u >= 0x37F and u <= 0x1FFF) or (u >= 0x200C and u <= 0x200D) or (u >= 0x203F and u <= 0x2040) 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": + if id_char in " \n\t\r": return NameValidityLevel.INVALID else: validity_level = NameValidityLevel.INVALID_XML_NAMETOKEN diff --git a/src/data_classes/AttributeColor.gd b/src/data_classes/AttributeColor.gd index 6f948d7..7f81fb3 100644 --- a/src/data_classes/AttributeColor.gd +++ b/src/data_classes/AttributeColor.gd @@ -1,12 +1,12 @@ -# An attribute representing a color string, or an url to the ID of a paint element. +## An attribute representing a color string or a url to the ID of a paint element. class_name AttributeColor extends Attribute # No direct color representation for this attribute type. There are too many quirks. func set_value(new_value: String) -> void: super(new_value if ColorParser.is_valid(new_value, false, - name in DB.attribute_color_url_allowed, name in DB.attribute_color_none_allowed, - name in DB.attribute_color_current_color_allowed) else "") + name in DB.COLOR_ATTRIBUTES_WITH_URL_ALLOWED, name in DB.COLOR_ATTRIBUTES_WITH_NONE_ALLOWED, + name in DB.COLOR_ATTRIBUTES_WITH_CURRENT_COLOR_ALLOWED) else "") func format(text: String, formatter: Formatter) -> String: text = text.strip_edges() @@ -68,16 +68,16 @@ func format(text: String, formatter: Formatter) -> String: text = named_color_text return text - +## Source: https://www.w3.org/TR/SVG11/types.html#ColorKeywords static func get_named_colors(include_alpha := false) -> Dictionary: if include_alpha: - var extended_named_colors := _named_colors.duplicate() + var extended_named_colors := _NAMED_COLORS.duplicate() extended_named_colors["transparent"] = "#00000000" return extended_named_colors else: - return _named_colors + return _NAMED_COLORS -const _named_colors: Dictionary[String, String] = { +const _NAMED_COLORS: Dictionary[String, String] = { "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "aqua": "#00ffff", diff --git a/src/data_classes/AttributeEnum.gd b/src/data_classes/AttributeEnum.gd index e28c59f..c1bdd88 100644 --- a/src/data_classes/AttributeEnum.gd +++ b/src/data_classes/AttributeEnum.gd @@ -1,5 +1,5 @@ -# An attribute with only a set of meaningful values. +## An attribute with only a set of meaningful values. class_name AttributeEnum extends Attribute func set_value(new_value: String) -> void: - super(new_value if (new_value.is_empty() or new_value in DB.attribute_enum_values[name]) else "") + super(new_value if (new_value.is_empty() or new_value in DB.ATTRIBUTE_ENUM_VALUES[name]) else "") diff --git a/src/data_classes/AttributeHref.gd b/src/data_classes/AttributeHref.gd index cdcb7c7..7e7ac7a 100644 --- a/src/data_classes/AttributeHref.gd +++ b/src/data_classes/AttributeHref.gd @@ -1,13 +1,13 @@ -# An attribute representing an element's href +## An attribute representing a reference to an ID. 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("#")) + return get_id_validity(id.trim_prefix("#")) diff --git a/src/data_classes/AttributeID.gd b/src/data_classes/AttributeID.gd index e769b3c..1daa861 100644 --- a/src/data_classes/AttributeID.gd +++ b/src/data_classes/AttributeID.gd @@ -1,4 +1,4 @@ -# An attribute representing an element's id +## An attribute representing an element's ID. class_name AttributeID extends Attribute func set_value(new_value: String) -> void: @@ -9,4 +9,4 @@ static func get_validity(id: String) -> NameValidityLevel: if id.is_empty() or id[0] == "#": return NameValidityLevel.INVALID - return get_name_validity(id) + return get_id_validity(id) diff --git a/src/data_classes/AttributeList.gd b/src/data_classes/AttributeList.gd index 2b8ccf1..b6ad558 100644 --- a/src/data_classes/AttributeList.gd +++ b/src/data_classes/AttributeList.gd @@ -1,4 +1,4 @@ -# An attribute representing a list of numbers. +## An attribute representing a list of numbers. class_name AttributeList extends Attribute var _list: PackedFloat64Array diff --git a/src/data_classes/AttributeNumeric.gd b/src/data_classes/AttributeNumeric.gd index 8903ad5..0f2e278 100644 --- a/src/data_classes/AttributeNumeric.gd +++ b/src/data_classes/AttributeNumeric.gd @@ -1,4 +1,4 @@ -# An attribute representing a number. +## An attribute representing a number. class_name AttributeNumeric extends Attribute var _percentage := false diff --git a/src/data_classes/AttributePathdata.gd b/src/data_classes/AttributePathdata.gd index 6291c36..6b48fae 100644 --- a/src/data_classes/AttributePathdata.gd +++ b/src/data_classes/AttributePathdata.gd @@ -1,4 +1,4 @@ -# The "d" attribute of ElementPath. +## The "d" attribute of ElementPath. class_name AttributePathdata extends Attribute var _commands: Array[PathCommand] @@ -208,8 +208,7 @@ func convert_command(idx: int, cmd_char: String) -> void: if conversion_made: sync_after_commands_change() -func convert_commands_optimized(indices: PackedInt32Array, -cmd_chars: PackedStringArray) -> void: +func convert_commands_optimized(indices: PackedInt32Array, cmd_chars: PackedStringArray) -> void: var conversions_made := false for i in indices.size(): var conversion_made := _convert_command(indices[i], cmd_chars[i]) diff --git a/src/data_classes/AttributeTransformList.gd b/src/data_classes/AttributeTransformList.gd index 8d433fa..8142cff 100644 --- a/src/data_classes/AttributeTransformList.gd +++ b/src/data_classes/AttributeTransformList.gd @@ -1,4 +1,4 @@ -# An attribute representing a list of transformations. +## An attribute representing a list of transformations. class_name AttributeTransformList extends Attribute var _transform_list: Array[Transform] = [] diff --git a/src/data_classes/BasicXNode.gd b/src/data_classes/BasicXNode.gd index 9fb0d9a..911905f 100644 --- a/src/data_classes/BasicXNode.gd +++ b/src/data_classes/BasicXNode.gd @@ -1,3 +1,4 @@ +## Any XML node that's not an element. class_name BasicXNode extends XNode signal text_changed # Any text change. diff --git a/src/data_classes/ColorParser.gd b/src/data_classes/ColorParser.gd index 58882e9..9c22ded 100644 --- a/src/data_classes/ColorParser.gd +++ b/src/data_classes/ColorParser.gd @@ -1,17 +1,25 @@ +## Parser for SVG colors. @abstract class_name ColorParser +## Adds a hash prefix to hex color strings if they don't already have one static func add_hash_if_hex(color: String) -> String: color = color.strip_edges() if color.is_valid_html_color() and not color.begins_with("#"): color = "#" + color return color +## Validates if a color string is in any supported format: "#RRGGBB", "#RGB", named color, rgb(r, g, b), hsl(h, s, l%). +## If allow_alpha is true, #RRGGBBAA, #RGBA, rgba(r, g, b, a(%?)), hsla(h, s, l%, a(%?)) are considered valid. +## If allow_url is true, "url(#id)" is considered valid. +## If allow_none is true, "none" is considered valid. +## If allow_current_color is true, "currentColor" is considered valid. static func is_valid(color: String, allow_alpha := false, allow_url := false, allow_none := false, allow_current_color := false) -> bool: return is_valid_hex(color, allow_alpha) or is_valid_rgb(color, allow_alpha) or is_valid_hsl(color, allow_alpha) or is_valid_named(color, allow_alpha) or\ (allow_url and is_valid_url(color)) or (allow_none and color.strip_edges() == "none") or\ (allow_current_color and color.strip_edges() == "currentColor") +## Validates if a color string is of #RRGGBB or #RGB format. If allow_alpha is true, also accepts #RRGGBBAA and #RGBA. static func is_valid_hex(color: String, allow_alpha := false) -> bool: color = color.strip_edges() if color.begins_with("#") and color.is_valid_html_color(): @@ -19,12 +27,18 @@ static func is_valid_hex(color: String, allow_alpha := false) -> bool: return true return false +## Validates if a color string is of rgb(r, g, b) format. If allow_alpha is true, also accepts rgba(r, g, b, a(%?)). static func is_valid_rgb(color: String, allow_alpha := false) -> bool: color = color.strip_edges() - if allow_alpha: - if _rgb_check(color): - return true - + var rgb_check := false + + if color.begins_with("rgb(") and color.ends_with(")"): + var channels_str := color.substr(4, color.length() - 5) + var channels := channels_str.split(",") + if channels.size() == 3: + rgb_check = _is_valid_number_or_percentage(channels[0]) and _is_valid_number_or_percentage(channels[1]) and _is_valid_number_or_percentage(channels[2]) + + if not rgb_check and allow_alpha: if not color.begins_with("rgba(") or not color.ends_with(")"): return false var channels_str := color.substr(5, color.length() - 6) @@ -33,71 +47,53 @@ static func is_valid_rgb(color: String, allow_alpha := false) -> bool: return false return _is_valid_number_or_percentage(channels[0]) and _is_valid_number_or_percentage(channels[1]) and\ _is_valid_number_or_percentage(channels[2]) and _is_valid_number_or_percentage(channels[3]) - else: - return _rgb_check(color) - -static func _rgb_check(stripped_color: String) -> bool: - if not stripped_color.begins_with("rgb(") or not stripped_color.ends_with(")"): - return false - - var channels_str := stripped_color.substr(4, stripped_color.length() - 5) - var channels := channels_str.split(",") - if channels.size() != 3: - return false - - return _is_valid_number_or_percentage(channels[0]) and _is_valid_number_or_percentage(channels[1]) and _is_valid_number_or_percentage(channels[2]) - + return rgb_check +## Validates if a color string is of hsl(h, s, l%) format. If allow_alpha is true, also accepts hsla(h, s, l, a(%?)). static func is_valid_hsl(color: String, allow_alpha := false) -> bool: color = color.strip_edges() - if allow_alpha: - if _hsl_check(color): - return true - + + var hsl_check := false + if color.begins_with("hsl(") and color.ends_with(")"): + var channels_str := color.substr(4, color.length() - 5) + var channels := channels_str.split(",") + if channels.size() == 3: + hsl_check = _is_valid_number(channels[0]) and _is_valid_percentage(channels[1]) and _is_valid_percentage(channels[2]) + + if not hsl_check and allow_alpha: if not color.begins_with("hsla(") or not color.ends_with(")"): return false - + var channels_str := color.substr(5, color.length() - 6) var channels := channels_str.split(",") if channels.size() != 4: return false - + return _is_valid_number(channels[0]) and _is_valid_percentage(channels[1]) and\ _is_valid_percentage(channels[2]) and _is_valid_number_or_percentage(channels[3]) - else: - return _hsl_check(color) - -static func _hsl_check(stripped_color: String) -> bool: - if not stripped_color.begins_with("hsl(") or not stripped_color.ends_with(")"): - return false - - var channels_str := stripped_color.substr(4, stripped_color.length() - 5) - var channels := channels_str.split(",") - if channels.size() != 3: - return false - - return _is_valid_number(channels[0]) and _is_valid_percentage(channels[1]) and _is_valid_percentage(channels[2]) + return hsl_check +## Validates if the color is in the list of SVG color keywords at https://www.w3.org/TR/SVG11/types.html#ColorKeywords. +## If allow_alpha is true, also accepts "transparent" static func is_valid_named(color: String, allow_alpha := false) -> bool: return AttributeColor.get_named_colors(allow_alpha).has(color.strip_edges()) +## Validates if a color string is of hsl(h, s, l%) format. If allow_alpha is true, also accepts hsla(h, s, l, a(%?)). 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)) != Attribute.NameValidityLevel.INVALID -static func _get_url_id(stripped_color: String) -> String: - return stripped_color.substr(4, stripped_color.length() - 5).strip_edges().right(-1) - -# URL doesn't have a color interpretation, so it'll give the backup. -static func text_to_color(color: String, backup := Color.BLACK, -allow_alpha := false) -> Color: +## Converts a color string into a Color if its format can be represented by a color. +## If allow_alpha is true, accepts formats with an alpha channel. +## If the text describes a format that's not accepted, returns the backup color. +static func text_to_color(color: String, backup := Color.BLACK, allow_alpha := false) -> Color: color = color.strip_edges() - if is_valid_named(color, allow_alpha): - return Color(AttributeColor.get_named_colors(allow_alpha)[color]) - elif color == "none": + if color == "none": return Color(0, 0, 0, 0) + elif is_valid_named(color): + return Color(AttributeColor.get_named_colors(allow_alpha)[color]) elif is_valid_rgb(color, allow_alpha): var args_start_pos := color.find("(") + 1 var inside_brackets := color.substr(args_start_pos, color.length() - args_start_pos - 1) @@ -138,6 +134,7 @@ allow_alpha := false) -> Color: else: return backup +## Compares two color strings by whether they represent the same color. static func are_colors_same(col1: String, col2: String) -> bool: col1 = col1.strip_edges() col2 = col2.strip_edges() @@ -156,13 +153,13 @@ static func are_colors_same(col1: String, col2: String) -> bool: # Represent both colors as 6-digit hex codes to serve as basis for comparison. for i in 2: - var col: String = [col1, col2][i] + var col := col1 if i == 0 else col2 # Start of conversion logic. if is_valid_rgb(col): col = text_to_color(col).to_html(false) elif is_valid_hex(col) and col.length() == 4: col = col[1] + col[1] + col[2] + col[2] + col[3] + col[3] - elif is_valid_named(col, true): + elif is_valid_named(col, false): col = AttributeColor.get_named_colors()[col] col = col.trim_prefix("#") # End of conversion logic. @@ -172,18 +169,6 @@ static func are_colors_same(col1: String, col2: String) -> bool: col2 = col return col1 == col2 -# Helpers -static func _is_valid_number_or_percentage(numstr: String) -> bool: - numstr = numstr.strip_edges() - return numstr.is_valid_float() or (not numstr.is_empty() and numstr[-1] == "%" and numstr.left(-1).is_valid_float()) - -static func _is_valid_number(numstr: String) -> bool: - return numstr.strip_edges().is_valid_float() - -static func _is_valid_percentage(numstr: String) -> bool: - numstr = numstr.strip_edges() - return not numstr.is_empty() and numstr[-1] == "%" and numstr.left(-1).is_valid_float() - static func hsl_get_r(h: int, s: float, l: float) -> int: return decompose_hsl(0, h, s, l) @@ -198,3 +183,19 @@ static func decompose_hsl(n: int, h: int, s: float, l: float) -> int: var k := fmod(n + h/30.0, 12) var a := s * minf(l, 1 - l) return roundi((l - a * maxf(-1, minf(minf(k - 3, 9 - k), 1))) * 255) + + +# Helpers +static func _is_valid_number_or_percentage(numstr: String) -> bool: + numstr = numstr.strip_edges() + return numstr.is_valid_float() or (not numstr.is_empty() and numstr[-1] == "%" and numstr.left(-1).is_valid_float()) + +static func _is_valid_number(numstr: String) -> bool: + return numstr.strip_edges().is_valid_float() + +static func _is_valid_percentage(numstr: String) -> bool: + numstr = numstr.strip_edges() + return not numstr.is_empty() and numstr[-1] == "%" and numstr.left(-1).is_valid_float() + +static func _get_url_id(stripped_color: String) -> String: + return stripped_color.substr(4, stripped_color.length() - 5).strip_edges().right(-1) diff --git a/src/data_classes/DB.gd b/src/data_classes/DB.gd index 873f61a..70000f8 100644 --- a/src/data_classes/DB.gd +++ b/src/data_classes/DB.gd @@ -1,3 +1,4 @@ +## A central database for SVG element and attribute definitions. @abstract class_name DB enum AttributeType {NUMERIC, COLOR, LIST, PATHDATA, ENUM, TRANSFORM_LIST, ID, HREF, UNKNOWN} @@ -5,10 +6,10 @@ 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", +const _RECOGNIZED_ELEMENTS: PackedStringArray = ["svg", "g", "circle", "ellipse", "rect", "path", "line", "polyline", "polygon", "stop", "linearGradient", "radialGradient", "use"] -const _element_icons: Dictionary[String, Texture2D] = { +const _ELEMENT_ICONS: Dictionary[String, Texture2D] = { "circle": preload("res://assets/icons/element/circle.svg"), "ellipse": preload("res://assets/icons/element/ellipse.svg"), "rect": preload("res://assets/icons/element/rect.svg"), @@ -23,16 +24,16 @@ const _element_icons: Dictionary[String, Texture2D] = { "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") +const _UNRECOGNIZED_XNODE_ICON = preload("res://assets/icons/element/unrecognized.svg") -const _xnode_icons: Dictionary[BasicXNode.NodeType, Texture2D] = { +const _XNODE_ICONS: Dictionary[BasicXNode.NodeType, Texture2D] = { BasicXNode.NodeType.COMMENT: preload("res://assets/icons/element/xmlnodeComment.svg"), BasicXNode.NodeType.TEXT: preload("res://assets/icons/element/xmlnodeText.svg"), BasicXNode.NodeType.CDATA: preload("res://assets/icons/element/xmlnodeCDATA.svg"), } -const recognized_attributes: Dictionary[String, Array] = { - # TODO this is just propagated_attributes, but it ruins the const because of Godot bug. +const _RECOGNIZED_ATTRIBUTES: Dictionary[String, Array] = { + # TODO this is just PROPAGATED_ATTRIBUTES, but it ruins the const because of Godot bug. # TODO Add "color" to "g" when we're ready. "svg": ["xmlns", "x", "y", "width", "height", "viewBox", "fill", "fill-opacity", "stroke", "stroke-opacity", "stroke-width", "stroke-linecap", "stroke-linejoin", "color"], @@ -60,7 +61,7 @@ const recognized_attributes: Dictionary[String, Array] = { "use": ["href", "transform", "x", "y"] } -const _valid_children: Dictionary[String, Array] = { +const _VALID_CHILDREN: Dictionary[String, Array] = { "svg": ["svg", "path", "circle", "ellipse", "rect", "line", "polygon", "polyline", "g", "linearGradient", "radialGradient", "use"], "g": ["svg", "path", "circle", "ellipse", "rect", "line", "polygon", "polyline", "g", "linearGradient", "radialGradient", "use"], "linearGradient": ["stop"], @@ -76,9 +77,10 @@ const _valid_children: Dictionary[String, Array] = { "use": [], } -const propagated_attributes: Array[String] = ["fill", "fill-opacity", "stroke", "stroke-opacity", "stroke-width", "stroke-linecap", "stroke-linejoin", "color"] +const PROPAGATED_ATTRIBUTES: PackedStringArray = ["fill", "fill-opacity", "stroke", "stroke-opacity", "stroke-width", "stroke-linecap", "stroke-linejoin", + "color"] -const _attribute_types: Dictionary[String, AttributeType] = { +const _ATTRIBUTE_TYPES: Dictionary[String, AttributeType] = { "viewBox": AttributeType.LIST, "width": AttributeType.NUMERIC, "height": AttributeType.NUMERIC, @@ -117,14 +119,14 @@ const _attribute_types: Dictionary[String, AttributeType] = { "href": AttributeType.HREF, } -const attribute_enum_values: Dictionary[String, Array] = { +const ATTRIBUTE_ENUM_VALUES: Dictionary[String, Array] = { "stroke-linecap": ["butt", "round", "square"], "stroke-linejoin": ["miter", "round", "bevel"], "gradientUnits": ["userSpaceOnUse", "objectBoundingBox"], "spreadMethod": ["pad", "reflect", "repeat"], } -const attribute_number_range: Dictionary[String, NumberRange] = { +const ATTRIBUTE_NUMBER_RANGE: Dictionary[String, NumberRange] = { "width": NumberRange.POSITIVE, "height": NumberRange.POSITIVE, "x": NumberRange.ARBITRARY, @@ -148,41 +150,49 @@ const attribute_number_range: Dictionary[String, NumberRange] = { "stop-opacity": NumberRange.UNIT, } -const attribute_color_url_allowed: Array[String] = ["fill", "stroke"] -const attribute_color_none_allowed: Array[String] = ["fill", "stroke"] -const attribute_color_current_color_allowed: Array[String] = ["fill", "stroke", "stop-color"] +const COLOR_ATTRIBUTES_WITH_URL_ALLOWED: PackedStringArray = ["fill", "stroke"] +const COLOR_ATTRIBUTES_WITH_NONE_ALLOWED: PackedStringArray = ["fill", "stroke"] +const COLOR_ATTRIBUTES_WITH_CURRENT_COLOR_ALLOWED: PackedStringArray = ["fill", "stroke", "stop-color"] +static func is_element_recognized(element_name: String) -> bool: + return _RECOGNIZED_ELEMENTS.has(element_name) +## Get all recognized attributes for a specific element. static func get_recognized_attributes(element_name: String) -> Array: - return recognized_attributes.get(element_name, []) + return _RECOGNIZED_ATTRIBUTES.get(element_name, []) +## Check if an attribute is recognized for a given element. static func is_attribute_recognized(element_name: String, attribute_name: String) -> bool: - return recognized_attributes.has(element_name) and attribute_name in recognized_attributes[element_name] + return _RECOGNIZED_ATTRIBUTES.has(element_name) and attribute_name in _RECOGNIZED_ATTRIBUTES[element_name] +## Check if the given child element is valid for the given parent element. static func is_child_element_valid(parent_name: String, child_name: String) -> bool: - if not parent_name in recognized_elements or not child_name in recognized_elements: + if not parent_name in _RECOGNIZED_ELEMENTS or not child_name in _RECOGNIZED_ELEMENTS: return true - return child_name in _valid_children[parent_name] + return child_name in _VALID_CHILDREN[parent_name] +## Get all valid parent elements for a given child element. static func get_valid_parents(child_name: String) -> PackedStringArray: var valid_parents := PackedStringArray() - for parent_name in _valid_children: - if child_name in _valid_children[parent_name]: + for parent_name in _VALID_CHILDREN: + if child_name in _VALID_CHILDREN[parent_name]: valid_parents.append(parent_name) return valid_parents +## Get the icon for an element type. static func get_element_icon(element_name: String) -> Texture2D: - return _element_icons.get(element_name, _unrecognized_xnode_icon) + return _ELEMENT_ICONS.get(element_name, _UNRECOGNIZED_XNODE_ICON) +## Get the icon for an XML node that's not an element. static func get_xnode_icon(xnode_type: BasicXNode.NodeType) -> Texture2D: - return _xnode_icons.get(xnode_type, _unrecognized_xnode_icon) - + return _XNODE_ICONS.get(xnode_type, _UNRECOGNIZED_XNODE_ICON) +## Get the data type for an attribute. static func get_attribute_type(attribute_name: String) -> AttributeType: - return _attribute_types.get(attribute_name, AttributeType.UNKNOWN) + return _ATTRIBUTE_TYPES.get(attribute_name, AttributeType.UNKNOWN) -static func get_attribute_default_percentage_handling( -attribute_name: String) -> PercentageHandling: +## Get default percentage handling behavior for numeric attributes. +static func get_attribute_default_percentage_handling(attribute_name: String) -> PercentageHandling: match attribute_name: "width": return PercentageHandling.HORIZONTAL "height": return PercentageHandling.VERTICAL @@ -202,12 +212,13 @@ attribute_name: String) -> PercentageHandling: "r": return PercentageHandling.NORMALIZED _: return PercentageHandling.FRACTION - +## Create an element with initial arbitrary setup values based on that element's user_setup() method. static func element_with_setup(name: String, user_setup_values: Array) -> Element: var new_element := element(name) new_element.user_setup.callv(user_setup_values) return new_element +## Factory method to create typed element instances. static func element(name: String) -> Element: match name: "svg": return ElementSVG.new() @@ -225,6 +236,7 @@ static func element(name: String) -> Element: "use": return ElementUse.new() _: return ElementUnrecognized.new(name) +## Factory method to create typed attribute instances. static func attribute(name: String, value: String) -> Attribute: match DB.get_attribute_type(name): DB.AttributeType.NUMERIC: return AttributeNumeric.new(name, value) diff --git a/src/data_classes/Element.gd b/src/data_classes/Element.gd index 75b4634..69547ec 100644 --- a/src/data_classes/Element.gd +++ b/src/data_classes/Element.gd @@ -1,4 +1,4 @@ -# Represents an element, either standalone () or container (...). +## Represents an element, either standalone () or container (...). @abstract class_name Element extends XNode signal attribute_changed(name: String) @@ -235,7 +235,7 @@ func set_attribute(attrib_name: String, value: Variant) -> void: push_error("Invalid value set to attribute.") func get_default(attribute_name: String) -> String: - if attribute_name in DB.propagated_attributes: + if attribute_name in DB.PROPAGATED_ATTRIBUTES: if is_parent_g(): return parent.get_implied_attribute_value(attribute_name) elif svg != null: diff --git a/src/data_classes/ElementBaseGradient.gd b/src/data_classes/ElementBaseGradient.gd index 006579a..c466027 100644 --- a/src/data_classes/ElementBaseGradient.gd +++ b/src/data_classes/ElementBaseGradient.gd @@ -1,3 +1,4 @@ +## An abstract class with common functionalities between linear and radial gradients. @abstract class_name ElementBaseGradient extends Element const possible_conversions: PackedStringArray = [] @@ -49,5 +50,5 @@ func get_config_warnings() -> PackedStringArray: return warnings - +## Generates a preview 64x64 texture of the gradient. @abstract func generate_texture() -> SVGTexture diff --git a/src/data_classes/ElementCircle.gd b/src/data_classes/ElementCircle.gd index 43fb968..e96c25e 100644 --- a/src/data_classes/ElementCircle.gd +++ b/src/data_classes/ElementCircle.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementCircle extends Element const name = "circle" diff --git a/src/data_classes/ElementEllipse.gd b/src/data_classes/ElementEllipse.gd index dc4e1d8..7f7451c 100644 --- a/src/data_classes/ElementEllipse.gd +++ b/src/data_classes/ElementEllipse.gd @@ -1,4 +1,4 @@ -# An element. +## An element. class_name ElementEllipse extends Element const name = "ellipse" diff --git a/src/data_classes/ElementG.gd b/src/data_classes/ElementG.gd index 4d2718a..8aaa117 100644 --- a/src/data_classes/ElementG.gd +++ b/src/data_classes/ElementG.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementG extends Element const name = "g" diff --git a/src/data_classes/ElementLine.gd b/src/data_classes/ElementLine.gd index 028d43e..9f22f31 100644 --- a/src/data_classes/ElementLine.gd +++ b/src/data_classes/ElementLine.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementLine extends Element const name = "line" @@ -24,16 +24,15 @@ func get_replacement(new_element: String) -> Element: match new_element: "polyline": dropped_attributes = PackedStringArray(["x1", "y1", "x2", "y2", "points"]) - var points := PackedFloat64Array([get_attribute_num("x1"), - get_attribute_num("y1"), get_attribute_num("x2"), get_attribute_num("y2")]) + var points := PackedFloat64Array([get_attribute_num("x1"), get_attribute_num("y1"), + get_attribute_num("x2"), get_attribute_num("y2")]) element.get_attribute("points").set_list(points) element.set_attribute("fill", "none") "path": element = ElementPath.new() dropped_attributes = PackedStringArray(["x1", "y1", "x2", "y2", "d"]) var commands: Array[PathCommand] = [] - commands.append(PathCommand.MoveCommand.new(get_attribute_num("x1"), - get_attribute_num("y1"), true)) + commands.append(PathCommand.MoveCommand.new(get_attribute_num("x1"), get_attribute_num("y1"), true)) commands.append(PathCommand.LineCommand.new( get_attribute_num("x2") - get_attribute_num("x1"), get_attribute_num("y2") - get_attribute_num("y1"), true)) diff --git a/src/data_classes/ElementLinearGradient.gd b/src/data_classes/ElementLinearGradient.gd index dcdd9ed..5434138 100644 --- a/src/data_classes/ElementLinearGradient.gd +++ b/src/data_classes/ElementLinearGradient.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementLinearGradient extends ElementBaseGradient const name = "linearGradient" @@ -18,8 +18,7 @@ func get_percentage_handling(attribute_name: String) -> DB.PercentageHandling: return super(attribute_name) func generate_texture() -> SVGTexture: - var svg_texture_text := """ element. +## A element. class_name ElementPath extends Element const name = "path" @@ -150,12 +150,10 @@ func get_bounding_box() -> Rect2: sc = -sc var ct := Vector2(r.x * sc * y1 / r.y, -r.y * sc * x1 / r.x) - var c := Vector2(ct.x * cosine - ct.y * sine, - ct.x * sine + ct.y * cosine) + start.lerp(end, 0.5) + var c := Vector2(ct.x * cosine - ct.y * sine, ct.x * sine + ct.y * cosine) + start.lerp(end, 0.5) var tv := Vector2(x1 - ct.x, y1 - ct.y) / r var theta1 := tv.angle() - var delta_theta := fposmod(tv.angle_to( - Vector2(-x1 - ct.x, -y1 - ct.y) / r), TAU) + var delta_theta := fposmod(tv.angle_to(Vector2(-x1 - ct.x, -y1 - ct.y) / r), TAU) if cmd.sweep_flag == 0: theta1 += delta_theta delta_theta = TAU - delta_theta diff --git a/src/data_classes/ElementPolygon.gd b/src/data_classes/ElementPolygon.gd index 89a8f49..95550d0 100644 --- a/src/data_classes/ElementPolygon.gd +++ b/src/data_classes/ElementPolygon.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementPolygon extends Element const name = "polygon" diff --git a/src/data_classes/ElementPolyline.gd b/src/data_classes/ElementPolyline.gd index 7232c3e..f96ed8b 100644 --- a/src/data_classes/ElementPolyline.gd +++ b/src/data_classes/ElementPolyline.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementPolyline extends Element const name = "polyline" diff --git a/src/data_classes/ElementRadialGradient.gd b/src/data_classes/ElementRadialGradient.gd index 93b5ba0..edf19f7 100644 --- a/src/data_classes/ElementRadialGradient.gd +++ b/src/data_classes/ElementRadialGradient.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementRadialGradient extends ElementBaseGradient const name = "radialGradient" diff --git a/src/data_classes/ElementRect.gd b/src/data_classes/ElementRect.gd index 59a120d..b750882 100644 --- a/src/data_classes/ElementRect.gd +++ b/src/data_classes/ElementRect.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementRect extends Element const name = "rect" diff --git a/src/data_classes/ElementRoot.gd b/src/data_classes/ElementRoot.gd index 7638465..98dd451 100644 --- a/src/data_classes/ElementRoot.gd +++ b/src/data_classes/ElementRoot.gd @@ -121,7 +121,7 @@ func move_xnodes_to(xids: Array[PackedInt32Array], location: PackedInt32Array) - xids = XIDUtils.filter_descendants(xids) # An element can't move deeper inside itself. Remove the descendants of the location. for i in range(xids.size() - 1, -1, -1): - if XIDUtils.is_parent(xids[i], location): + if XIDUtils.is_ancestor(xids[i], location): xids.remove_at(i) # Remove elements from their old locations. diff --git a/src/data_classes/ElementSVG.gd b/src/data_classes/ElementSVG.gd index edd3925..8ba5a75 100644 --- a/src/data_classes/ElementSVG.gd +++ b/src/data_classes/ElementSVG.gd @@ -1,4 +1,4 @@ -# An element. +## An element. class_name ElementSVG extends Element var width: float diff --git a/src/data_classes/ElementStop.gd b/src/data_classes/ElementStop.gd index 8723fe6..7cad7d9 100644 --- a/src/data_classes/ElementStop.gd +++ b/src/data_classes/ElementStop.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementStop extends Element const name = "stop" diff --git a/src/data_classes/ElementUse.gd b/src/data_classes/ElementUse.gd index 661077b..dbc67a7 100644 --- a/src/data_classes/ElementUse.gd +++ b/src/data_classes/ElementUse.gd @@ -1,4 +1,4 @@ -# A element. +## A element. class_name ElementUse extends Element const name = "use" diff --git a/src/data_classes/ListParser.gd b/src/data_classes/ListParser.gd index cb20be9..ff654e3 100644 --- a/src/data_classes/ListParser.gd +++ b/src/data_classes/ListParser.gd @@ -1,10 +1,11 @@ -# A parser to turn AttributeList's list into other useful things. +## A parser that converts between AttributeList's list and other representations of it. @abstract class_name ListParser +## Converts a rect (probably a viewbox) into a list of floats. static func rect_to_list(rect: Rect2) -> PackedFloat64Array: return PackedFloat64Array([rect.position.x, rect.position.y, rect.size.x, rect.size.y]) -# Converts the list of floats into a list of Vector2s. +## Converts a list of floats into a Vector2 array. static func list_to_points(list: PackedFloat64Array) -> PackedVector2Array: var points := PackedVector2Array() @warning_ignore("integer_division") diff --git a/src/data_classes/NumberParser.gd b/src/data_classes/NumberParser.gd index fc59f7d..131d70a 100644 --- a/src/data_classes/NumberParser.gd +++ b/src/data_classes/NumberParser.gd @@ -1,6 +1,7 @@ -# Common parser for AttributeNumeric and AttributeList +## Parser for AttributeNumeric and individual elements in AttributeList @abstract class_name NumberParser +## Converts a single number into a string based on a formatter. static func num_to_text(number: float, formatter: Formatter) -> String: if not is_finite(number): return "" diff --git a/src/data_classes/NumstringParser.gd b/src/data_classes/NumstringParser.gd index 5682e90..62c2a0c 100644 --- a/src/data_classes/NumstringParser.gd +++ b/src/data_classes/NumstringParser.gd @@ -1,17 +1,19 @@ -# A parser for compressed number arrays, used in pathdata and transform lists. -# Also for general utility around parsing numeric text. -# This parser has situational configuration options, so it isn't abstract. +## Parser for numeric text, including the compressed number arrays used in pathdata and transform lists. +## This parser has situational configuration options, so it's not abstract. class_name NumstringParser var compress_numbers: bool var minimize_spacing: bool +## Converts a number into text for the individual fields representing path parameters. +## Those aren't represent anything directly, so they are free to be made readable and not care about formatters. static func basic_num_to_text(num: float, is_angle := false) -> String: var text := Utils.num_simple(num, Utils.MAX_ANGLE_PRECISION if is_angle else Utils.MAX_NUMERIC_PRECISION) if text == "-0": text = "0" return text +## Converts a number into text based on the parser configuration. func num_to_text(num: float, is_angle := false) -> String: var text := Utils.num_simple(num, Utils.MAX_ANGLE_PRECISION if is_angle else Utils.MAX_NUMERIC_PRECISION) if compress_numbers: @@ -23,6 +25,7 @@ func num_to_text(num: float, is_angle := false) -> String: text = "0" return text +## Combines an array of numeric strings based on the parser configuration. func numstr_arr_to_text(numstr_arr: PackedStringArray) -> String: var output := "" for i in numstr_arr.size() - 1: @@ -34,18 +37,14 @@ func numstr_arr_to_text(numstr_arr: PackedStringArray) -> String: return output + numstr_arr[-1] -# This function evaluates expressions even if "," or ";" is used as a decimal separator. +## Evaluates expressions, so that when the user types "-2.2 + 4" or "sqrt(2)" it can be evaluated by GodSVG. +## Also works when "," is used as a decimal separator. static func evaluate(text: String) -> float: text = text.strip_edges() text = text.trim_prefix("+") # Expression can't handle unary plus. var expr := Expression.new() var err := expr.parse(text.replace(",", ".")) - if err == OK: - var result: Variant = expr.execute() - if not expr.has_execute_failed() and typeof(result) in [TYPE_FLOAT, TYPE_INT]: - return result - err = expr.parse(text.replace(";", ".")) if err == OK: var result: Variant = expr.execute() if not expr.has_execute_failed() and typeof(result) in [TYPE_FLOAT, TYPE_INT]: @@ -71,11 +70,9 @@ enum NumberJumbleParseState { INSIDE_NUMBER_INDIRECTLY_AFTER_EXPONENT } -# Returns an array with a PackedFloat64Array and an int for the final current index. -# Returns an empty array if there's a parsing error. # TODO In 4.5, I had to avoid the match keyword in this parser for performance: #75682. -static func text_to_number_arr(text: String, current_index: int, expected_count: int, -allow_starting_comma := false) -> Array: +## Returns an array with a PackedFloat64Array and an int for the final current index. Returns an empty array if there's a parsing error. +static func text_to_number_arr(text: String, current_index: int, expected_count: int, allow_starting_comma := false) -> Array: var text_length := text.length() if current_index >= text_length: return [] diff --git a/src/data_classes/SVGParser.gd b/src/data_classes/SVGParser.gd index 243f978..92fe6e3 100644 --- a/src/data_classes/SVGParser.gd +++ b/src/data_classes/SVGParser.gd @@ -1,11 +1,12 @@ +## Parser for SVG markup. @abstract class_name SVGParser -## Checks if the text describes an SVG without child elements. -static func text_check_is_root_empty(text: String) -> bool: - if text.is_empty(): +## Checks if the SVG markup describes an SVG element without child elements. +static func markup_check_is_root_empty(markup: String) -> bool: + if markup.is_empty(): return false - var buffer := text.to_utf8_buffer() + var buffer := markup.to_utf8_buffer() var parser := XMLParser.new() parser.open_buffer(buffer) @@ -27,65 +28,71 @@ static func text_check_is_root_empty(text: String) -> bool: # If the SVG tag isn't immediately closed, then check if the next XML node is an element end. return describes_svg and parser.read() == OK and parser.get_node_type() == XMLParser.NODE_ELEMENT_END and parser.get_node_name() == "svg" -## Creates a new SVG with a custom viewport that shows only a rectangular section of the original. -static func root_cutout_to_text(root_element: ElementRoot, custom_width: float, custom_height: float, custom_viewbox: Rect2) -> String: - # Build a new root element, set it up, and convert it to text. +## Creates markup for an SVG that only represents a rectangular cutout of the original. +## Returns a [code][markup, inner_markup][/code] array. The inner markup can then be fed back into [param cached_inner_markup], +## assuming the inner contents of [param root_element] haven't changed, for improved performance. +static func root_cutout_to_markup(root_element: ElementRoot, custom_width: float, custom_height: float, custom_viewbox: Rect2, cached_inner_markup: String = "") -> PackedStringArray: + # Build a new root element, set it up, and convert it to markup. var new_root_element: ElementRoot = root_element.duplicate(false) new_root_element.set_attribute("viewBox", ListParser.rect_to_list(custom_viewbox)) new_root_element.set_attribute("width", custom_width) new_root_element.set_attribute("height", custom_height) - var text := _xnode_to_text(new_root_element, Configs.savedata.editor_formatter) - # Since we only converted a single root element to text, it would have closed. - # Remove the closure and add all the other elements' text before closing it manually. - text = text.left(maxi(text.find("/>"), text.find(""))) + ">" - for child_idx in root_element.get_child_count(): - text += _xnode_to_text(root_element.get_xnode(PackedInt32Array([child_idx])), Configs.savedata.editor_formatter, true) - return text + "" + var markup := _xnode_to_markup(new_root_element, Configs.savedata.editor_formatter) + # Since we only converted a single root element to markup, it would have closed. + # Remove the closure and add all the other elements' markup before closing it manually. + markup = markup.left(maxi(markup.find("/>"), markup.find(""))) + ">" + var inner_markup := cached_inner_markup + if not cached_inner_markup: + for child_idx in root_element.get_child_count(): + inner_markup += _xnode_to_markup(root_element.get_xnode(PackedInt32Array([child_idx])), Configs.savedata.editor_formatter, true) + return [markup + inner_markup + "", inner_markup] -## Converts the child elements of a root element into text, excluding the svg tag itself. -static func root_children_to_text(root_element: ElementRoot, formatter: Formatter) -> String: - var text := "" +## Converts the child elements of a root element into markup, excluding the root tag itself. +static func root_children_to_markup(root_element: ElementRoot, formatter: Formatter) -> String: + var markup := "" for child in root_element.get_children(): - var new_text := _xnode_to_text(child, formatter) + var new_markup := _xnode_to_markup(child, formatter) # Remove one level of indentation from each line to maintain proper formatting. - var lines := new_text.split('\n') + var lines := new_markup.split('\n') for i in lines.size(): lines[i] = lines[i].trim_prefix(formatter.get_indent_string()) - text += '\n'.join(lines) - return text.trim_suffix('\n') + markup += '\n'.join(lines) + return markup.trim_suffix('\n') -static func root_to_editor_text(root_element: ElementRoot) -> String: - return root_to_text(root_element, Configs.savedata.editor_formatter) +## Converts a root element into markup using the editor formatter. +static func root_to_editor_markup(root_element: ElementRoot) -> String: + return root_to_markup(root_element, Configs.savedata.editor_formatter) -static func root_to_export_text(root_element: ElementRoot) -> String: - return root_to_text(root_element, Configs.savedata.export_formatter) +## Converts a root element into markup using the export formatter. +static func root_to_export_markup(root_element: ElementRoot) -> String: + return root_to_markup(root_element, Configs.savedata.export_formatter) -## Converts a root element into text using a specific formatter. -static func root_to_text(root_element: ElementRoot, formatter: Formatter) -> String: - var text := _xnode_to_text(root_element, formatter).trim_suffix('\n') +## Converts a root element into markup using an arbitrary formatter. +static func root_to_markup(root_element: ElementRoot, formatter: Formatter) -> String: + var markup := _xnode_to_markup(root_element, formatter).trim_suffix('\n') if formatter.xml_add_trailing_newline: - text += "\n" - return text + markup += "\n" + return markup -## The main entry point for converting any XML node and its descendants into text. -## If make_attributes_absolute is true, converts percentage-based attributes into absolute values so cutouts can be safely made. -static func _xnode_to_text(xnode: XNode, formatter: Formatter, make_attributes_absolute := false) -> String: - var text := "" +# The main entry point for converting any XML node and its descendants into markup. +# If make_attributes_absolute is true, converts percentage-based attributes into absolute values so cutouts can be safely made. +static func _xnode_to_markup(xnode: XNode, formatter: Formatter, make_attributes_absolute := false) -> String: + var markup := "" if formatter.xml_pretty_formatting: - text = formatter.get_indent_string().repeat(xnode.xid.size()) + markup = formatter.get_indent_string().repeat(xnode.xid.size()) if not xnode.is_element(): if (not formatter.xml_keep_comments and xnode.get_type() == BasicXNode.NodeType.COMMENT): return "" match xnode.get_type(): - BasicXNode.NodeType.COMMENT: text += "" % xnode.get_text() - BasicXNode.NodeType.CDATA: text += "" % xnode.get_text() - _: text += xnode.get_text() + BasicXNode.NodeType.COMMENT: markup += "" % xnode.get_text() + BasicXNode.NodeType.CDATA: markup += "" % xnode.get_text() + _: markup += xnode.get_text() if formatter.xml_pretty_formatting: - text += "\n" - return text + markup += "\n" + return markup var element := xnode as Element var attribute_array := element.get_all_attributes() @@ -113,32 +120,32 @@ static func _xnode_to_text(xnode: XNode, formatter: Formatter, make_attributes_a new_attrib.set_num(element.get_attribute_num(attrib.name)) attribute_array[attrib_idx] = new_attrib - text += '<' + element.name + markup += '<' + element.name for attribute: Attribute in attribute_array: var value := attribute.get_formatted_value(formatter) if not '"' in value: - text += ' %s="%s"' % [attribute.name, value] + markup += ' %s="%s"' % [attribute.name, value] else: - text += " %s='%s'" % [attribute.name, value] + markup += " %s='%s'" % [attribute.name, value] if not element.has_children() and (formatter.xml_shorthand_tags == Formatter.ShorthandTags.ALWAYS or\ - (formatter.xml_shorthand_tags == Formatter.ShorthandTags.ALL_EXCEPT_CONTAINERS and not element.name in Formatter.container_elements)): - text += ' />' if formatter.xml_shorthand_tags_space_out_slash else '/>' + (formatter.xml_shorthand_tags == Formatter.ShorthandTags.ALL_EXCEPT_CONTAINERS and not element.name in Formatter.CONTAINER_ELEMENTS)): + markup += ' />' if formatter.xml_shorthand_tags_space_out_slash else '/>' if formatter.xml_pretty_formatting: - text += '\n' + markup += '\n' else: - text += '>' + markup += '>' if formatter.xml_pretty_formatting: - text += '\n' + markup += '\n' for child in element.get_children(): - text += _xnode_to_text(child, formatter, make_attributes_absolute) + markup += _xnode_to_markup(child, formatter, make_attributes_absolute) if formatter.xml_pretty_formatting: - text += formatter.get_indent_string().repeat(element.xid.size()) - text += '' % element.name + markup += formatter.get_indent_string().repeat(element.xid.size()) + markup += '' % element.name if formatter.xml_pretty_formatting: - text += '\n' - return text + markup += '\n' + return markup enum ParseError {OK, ERR_NOT_SVG, ERR_IMPROPER_NESTING} @@ -151,7 +158,8 @@ class ParseResult: error = err_id svg = result -static func get_error_string(parse_error: ParseError) -> String: +## Returns the human-readable text that should be shown to users to represent a ParseError. +static func get_parsing_error_string(parse_error: ParseError) -> String: match parse_error: ParseError.ERR_NOT_SVG: return Translator.translate("Doesn’t describe an SVG.") @@ -161,11 +169,11 @@ static func get_error_string(parse_error: ParseError) -> String: ## The main entry point for converting SVG markup into the internal element tree structure. ## Returns a parse result, which contains either the ElementRoot or an error from an enum. -static func text_to_root(text: String) -> ParseResult: - if text.is_empty(): +static func markup_to_root(markup: String) -> ParseResult: + if markup.is_empty(): return ParseResult.new(ParseError.ERR_NOT_SVG) - var buffer := text.to_utf8_buffer() + var buffer := markup.to_utf8_buffer() var root_element := ElementRoot.new() var parser := XMLParser.new() parser.open_buffer(buffer) diff --git a/src/shaders/animated_stroke.gdshader b/src/shaders/animated_stroke.gdshader index 27c8d02..0587fb3 100644 --- a/src/shaders/animated_stroke.gdshader +++ b/src/shaders/animated_stroke.gdshader @@ -35,7 +35,7 @@ void fragment() { vec2 uv = UV; vec2 fw = fwidth(uv); - float adjusted_ant_width = min(corrected_width, min(0.5 / fw.x, 0.5 / fw.y)) + 0.15; + float adjusted_ant_width = min(corrected_width, min(0.5 / fw.x, 0.5 / fw.y)); vec2 aw = fw * adjusted_ant_width; vec2 cond = (sign(abs(uv - 0.5) - 0.5 + aw) + 1.0) * 0.5 * ceil(((sign(uv.yx - aw.yx) + 1.0) * 0.5 * (sign(uv - 0.5) * vec2(0.5, -0.5) + 0.5) * 0.5 + (sign(1.0 - aw.yx - uv.yx) + 1.0) * 0.5 * (sign(uv - 0.5) * vec2(-0.5, 0.5)+ 0.5) * 0.5)); diff --git a/src/shaders/animated_stroke_static.gdshader b/src/shaders/animated_stroke_static.gdshader index 15bbe7d..9052473 100644 --- a/src/shaders/animated_stroke_static.gdshader +++ b/src/shaders/animated_stroke_static.gdshader @@ -35,7 +35,7 @@ void fragment() { vec2 uv = UV; vec2 fw = fwidth(uv); - float adjusted_ant_width = min(corrected_width, min(0.5 / fw.x, 0.5 / fw.y)) + 0.15; + float adjusted_ant_width = min(corrected_width, min(0.5 / fw.x, 0.5 / fw.y)); vec2 aw = fw * adjusted_ant_width; vec2 cond = (sign(abs(uv - 0.5) - 0.5 + aw) + 1.0) * 0.5 * ceil(((sign(uv.yx - aw.yx) + 1.0) * 0.5 * (sign(uv - 0.5) * vec2(0.5, -0.5) + 0.5) * 0.5 + (sign(1.0 - aw.yx - uv.yx) + 1.0) * 0.5 * (sign(uv - 0.5) * vec2(-0.5, 0.5)+ 0.5) * 0.5)); diff --git a/src/ui_parts/code_editor.gd b/src/ui_parts/code_editor.gd index 3cf6937..14fdf92 100644 --- a/src/ui_parts/code_editor.gd +++ b/src/ui_parts/code_editor.gd @@ -11,6 +11,10 @@ func _ready() -> void: shortcuts.add_shortcut("copy_svg_text", func() -> void: DisplayServer.clipboard_set(State.svg_text)) HandlerGUI.register_shortcuts(self, shortcuts) + options_button.pressed.connect(_on_options_button_pressed) + code_edit.text_changed.connect(_on_svg_code_edit_text_changed) + code_edit.focus_entered.connect(_on_svg_code_edit_focus_entered) + code_edit.focus_exited.connect(_on_svg_code_edit_focus_exited) Configs.theme_changed.connect(sync_theming) sync_theming() State.parsing_finished.connect(update_error) @@ -35,7 +39,7 @@ func update_error(err_id: SVGParser.ParseError) -> void: # When the error is shown, the code editor's theme is changed to match up. if not error_bar.visible: error_bar.show() - error_label.text = SVGParser.get_error_string(err_id) + error_label.text = SVGParser.get_parsing_error_string(err_id) sync_theming() func sync_theming() -> void: diff --git a/src/ui_parts/code_editor.tscn b/src/ui_parts/code_editor.tscn index 1d9d116..41d6fa2 100644 --- a/src/ui_parts/code_editor.tscn +++ b/src/ui_parts/code_editor.tscn @@ -55,8 +55,3 @@ layout_mode = 2 theme_override_fonts/normal_font = ExtResource("2_p4nol") theme_override_font_sizes/normal_font_size = 14 fit_content = true - -[connection signal="pressed" from="PanelContainer/CodeButtons/MetaActions/OptionsButton" to="." method="_on_options_button_pressed"] -[connection signal="focus_entered" from="ScriptEditor/SVGCodeEdit" to="." method="_on_svg_code_edit_focus_entered"] -[connection signal="focus_exited" from="ScriptEditor/SVGCodeEdit" to="." method="_on_svg_code_edit_focus_exited"] -[connection signal="text_changed" from="ScriptEditor/SVGCodeEdit" to="." method="_on_svg_code_edit_text_changed"] diff --git a/src/ui_parts/debug_content.gd b/src/ui_parts/debug_content.gd new file mode 100644 index 0000000..b4c637c --- /dev/null +++ b/src/ui_parts/debug_content.gd @@ -0,0 +1,56 @@ +extends MarginContainer + +@onready var debug_label: Label = $DebugContainer/DebugLabel +@onready var input_debug_label: Label = $DebugContainer/InputDebugLabel +@onready var debug_container: VBoxContainer = $DebugContainer + +func _ready() -> void: + var shortcuts := ShortcutsRegistration.new() + shortcuts.add_shortcut("debug", toggle_debug_visibility) + HandlerGUI.register_shortcuts(self, shortcuts) + + set_debug_visibility(false) + get_window().window_input.connect(_update_input_debug) + +func toggle_debug_visibility() -> void: + set_debug_visibility(not debug_container.visible) + +func set_debug_visibility(visibility: bool) -> void: + debug_container.visible = visibility + if visible: + input_debug_label.text = "" + update_debug() + +# The strings here are intentionally not localized. +func update_debug() -> void: + var debug_text := "" + debug_text += "FPS: %d\n" % Performance.get_monitor(Performance.TIME_FPS) + debug_text += "Static Mem: %s\n" % String.humanize_size(int(Performance.get_monitor(Performance.MEMORY_STATIC))) + debug_text += "Nodes: %d\n" % Performance.get_monitor(Performance.OBJECT_NODE_COUNT) + debug_text += "Stray nodes: %d\n" % Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) + debug_text += "Objects: %d\n" % Performance.get_monitor(Performance.OBJECT_COUNT) + debug_label.text = debug_text + # Set up the next update if the container is still visible. + if visible: + get_tree().create_timer(1.0).timeout.connect(update_debug) + +var last_event_text := "" +var last_event_repeat_count := 1 + +func _update_input_debug(event: InputEvent) -> void: + if visible and event.is_pressed(): + var new_text := input_debug_label.text + var event_text := event.as_text() + if event is InputEventMouse: + event_text += " (" + String.num(event.position.x, 2) + ", " + String.num(event.position.y, 2) + ")" + if event_text == last_event_text: + last_event_repeat_count += 1 + new_text = new_text.left(new_text.rfind("\n", new_text.length() - 2) + 1) + event_text += " (%d)" % last_event_repeat_count + else: + last_event_text = event_text + last_event_repeat_count = 1 + if new_text.count("\n") >= 5: + new_text = new_text.right(-new_text.find("\n") - 1) + new_text += event_text + "\n" + input_debug_label.text = new_text diff --git a/src/ui_parts/debug_content.gd.uid b/src/ui_parts/debug_content.gd.uid new file mode 100644 index 0000000..3dbac05 --- /dev/null +++ b/src/ui_parts/debug_content.gd.uid @@ -0,0 +1 @@ +uid://mtwptdpc3cxs diff --git a/src/ui_parts/debug_content.tscn b/src/ui_parts/debug_content.tscn new file mode 100644 index 0000000..f414d69 --- /dev/null +++ b/src/ui_parts/debug_content.tscn @@ -0,0 +1,38 @@ +[gd_scene load_steps=2 format=3 uid="uid://vrmbmt18wocm"] + +[ext_resource type="Script" uid="uid://mtwptdpc3cxs" path="res://src/ui_parts/debug_content.gd" id="1_7pqvx"] + +[node name="DebugContent" type="MarginContainer"] +anchors_preset = -1 +offset_top = 4.0 +offset_right = 10.0 +offset_bottom = 40.0 +size_flags_horizontal = 8 +size_flags_vertical = 0 +mouse_filter = 2 +theme_override_constants/margin_top = 36 +theme_override_constants/margin_right = 10 +script = ExtResource("1_7pqvx") + +[node name="DebugContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = -18 + +[node name="DebugLabel" type="Label" parent="DebugContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(1, 1, 1, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/line_spacing = 0 +theme_override_constants/outline_size = 4 +theme_override_font_sizes/font_size = 14 +horizontal_alignment = 2 + +[node name="InputDebugLabel" type="Label" parent="DebugContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.75, 0.75, 0.75, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/line_spacing = 0 +theme_override_constants/outline_size = 4 +theme_override_font_sizes/font_size = 14 +horizontal_alignment = 2 diff --git a/src/ui_parts/display.gd b/src/ui_parts/display.gd index cf887af..fde49a0 100644 --- a/src/ui_parts/display.gd +++ b/src/ui_parts/display.gd @@ -1,37 +1,45 @@ extends VBoxContainer const NumberEdit = preload("res://src/ui_widgets/number_edit.gd") +const ZoomWidget = preload("res://src/ui_widgets/zoom_widget.gd") +const MainCanvas = preload("res://src/ui_parts/main_canvas.gd") -@onready var viewport: SubViewport = %Viewport -@onready var reference_texture: TextureRect = %Viewport/ReferenceTexture +@onready var canvas: MainCanvas = $ViewportPanel/VBoxContainer/Canvas @onready var reference_button: Button = %ViewportOptions/Reference @onready var snapper: NumberEdit = %ViewportOptions/Snapping/SnapNumberEdit @onready var snap_button: BetterButton = %ViewportOptions/Snapping/SnapButton @onready var viewport_panel: PanelContainer = $ViewportPanel -@onready var debug_container: MarginContainer = $ViewportPanel/DebugMargins -@onready var debug_label: Label = %DebugContainer/DebugLabel -@onready var input_debug_label: Label = %DebugContainer/InputDebugLabel @onready var toolbar: PanelContainer = $ViewportPanel/VBoxContainer/Toolbar +@onready var zoom_widget: ZoomWidget = %ZoomMenu var tabs_panel: PanelContainer func _ready() -> void: + var shortcuts := ShortcutsRegistration.new() + shortcuts.add_shortcut("load_reference", FileUtils.open_image_import_dialog.bind(set_main_viewport_reference_image), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("view_show_reference", func() -> void: canvas.show_reference = not canvas.show_reference, + ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + shortcuts.add_shortcut("view_overlay_reference", func() -> void: canvas.overlay_reference = not canvas.overlay_reference, + ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + shortcuts.add_shortcut("toggle_snap", func() -> void: Configs.savedata.snap *= -1, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + HandlerGUI.register_shortcuts(self, shortcuts) + + zoom_widget.setup_limits(Canvas.MIN_ZOOM, Canvas.MAX_ZOOM) + zoom_widget.zoom_in_pressed.connect(canvas.zoom_in) + zoom_widget.zoom_out_pressed.connect(canvas.zoom_out) + zoom_widget.zoom_reset_pressed.connect(canvas.center_frame) + canvas.camera_zoom_changed.connect(func() -> void: zoom_widget.sync_to_value(canvas.camera_zoom)) + + reference_button.pressed.connect(_on_reference_button_pressed) + snap_button.toggled.connect(_on_snap_button_toggled) + snapper.value_changed.connect(_on_snap_number_edit_value_changed) Configs.language_changed.connect(sync_localization) sync_localization() Configs.snap_changed.connect(update_snap_config) update_snap_config() Configs.theme_changed.connect(sync_theming) sync_theming() - Configs.active_tab_changed.connect(sync_reference_image) - Configs.active_tab_reference_changed.connect(sync_reference_image) - sync_reference_image() - State.show_reference_changed.connect(_on_show_reference_updated) - _on_show_reference_updated() - State.overlay_reference_changed.connect(_on_overlay_reference_updated) - _on_overlay_reference_updated() - State.show_debug_changed.connect(_on_show_debug_changed) - _on_show_debug_changed() - get_window().window_input.connect(_update_input_debug) func sync_localization() -> void: @@ -50,16 +58,17 @@ func update_snap_config() -> void: snapper.editable = snap_enabled snapper.set_value(absf(snap_config)) - -func _on_reference_pressed() -> void: - var has_reference := is_instance_valid(reference_texture.texture) +func _on_reference_button_pressed() -> void: + var active_tab := Configs.savedata.get_active_tab() + var has_reference := is_instance_valid(active_tab.reference_image) var btn_arr: Array[Button] = [ ContextPopup.create_shortcut_button("load_reference"), ContextPopup.create_button(Translator.translate("Paste reference image"), - paste_reference_image, not Utils.has_clipboard_image_web_safe(), - load("res://assets/icons/Paste.svg")), - ContextPopup.create_shortcut_checkbox("view_show_reference", State.show_reference and has_reference, not has_reference), - ContextPopup.create_shortcut_checkbox("view_overlay_reference", State.overlay_reference and has_reference, not has_reference) + paste_reference_image, not Utils.has_clipboard_image_web_safe(), load("res://assets/icons/Paste.svg")), + ContextPopup.create_button(Translator.translate("Clear reference image"), + clear_reference_image, not has_reference, load("res://assets/icons/Clear.svg")), + ContextPopup.create_shortcut_checkbox("view_show_reference", active_tab.show_reference and has_reference, not has_reference), + ContextPopup.create_shortcut_checkbox("view_overlay_reference", active_tab.overlay_reference and has_reference, not has_reference) ] var reference_popup := ContextPopup.new() @@ -67,72 +76,20 @@ func _on_reference_pressed() -> void: HandlerGUI.popup_under_rect_center(reference_popup, reference_button.get_global_rect(), get_viewport()) func paste_reference_image() -> void: - FileUtils.load_reference_from_image(DisplayServer.clipboard_get_image()) + set_main_viewport_reference_image(DisplayServer.clipboard_get_image()) +func clear_reference_image() -> void: + set_main_viewport_reference_image(null) -func _on_show_reference_updated() -> void: - reference_texture.visible = State.show_reference - -func _on_overlay_reference_updated() -> void: - if State.overlay_reference: - viewport.move_child(reference_texture, viewport.get_child_count() - 1) +func set_main_viewport_reference_image(image: Image) -> void: + if is_instance_valid(image): + canvas.reference_image = ImageTexture.create_from_image(image) else: - viewport.move_child(reference_texture, 0) + canvas.reference_image = null -func sync_reference_image() -> void: - var reference := Configs.savedata.get_active_tab().reference_image - if is_instance_valid(reference): - reference_texture.texture = reference - reference_texture.show() - else: - reference_texture.texture = null - reference_texture.hide() func _on_snap_button_toggled(toggled_on: bool) -> void: Configs.savedata.snap = absf(Configs.savedata.snap) if toggled_on else -absf(Configs.savedata.snap) func _on_snap_number_edit_value_changed(new_value: float) -> void: Configs.savedata.snap = new_value * signf(Configs.savedata.snap) - - -func _on_show_debug_changed() -> void: - if State.show_debug: - debug_container.show() - update_debug() - input_debug_label.text = "" - else: - debug_container.hide() - -# The strings here are intentionally not localized. -func update_debug() -> void: - var debug_text := "" - debug_text += "FPS: %d\n" % Performance.get_monitor(Performance.TIME_FPS) - debug_text += "Static Mem: %s\n" % String.humanize_size(int(Performance.get_monitor(Performance.MEMORY_STATIC))) - debug_text += "Nodes: %d\n" % Performance.get_monitor(Performance.OBJECT_NODE_COUNT) - debug_text += "Stray nodes: %d\n" % Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT) - debug_text += "Objects: %d\n" % Performance.get_monitor(Performance.OBJECT_COUNT) - debug_label.text = debug_text - # Set up the next update if the container is still visible. - if debug_container.visible: - get_tree().create_timer(1.0).timeout.connect(update_debug) - -var last_event_text := "" -var last_event_repeat_count := 1 - -func _update_input_debug(event: InputEvent) -> void: - if debug_container.visible and event.is_pressed(): - var new_text := input_debug_label.text - var event_text := event.as_text() - if event is InputEventMouse: - event_text += " (" + String.num(event.position.x, 2) + ", " + String.num(event.position.y, 2) + ")" - if event_text == last_event_text: - last_event_repeat_count += 1 - new_text = new_text.left(new_text.rfind("\n", new_text.length() - 2) + 1) - event_text += " (%d)" % last_event_repeat_count - else: - last_event_text = event_text - last_event_repeat_count = 1 - if new_text.count("\n") >= 5: - new_text = new_text.right(-new_text.find("\n") - 1) - new_text += event_text + "\n" - input_debug_label.text = new_text diff --git a/src/ui_parts/display.tscn b/src/ui_parts/display.tscn index 7ae0ad0..a560525 100644 --- a/src/ui_parts/display.tscn +++ b/src/ui_parts/display.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=18 format=3 uid="uid://bvrncl7e6yn5b"] +[gd_scene load_steps=13 format=3 uid="uid://bvrncl7e6yn5b"] [ext_resource type="Script" uid="uid://bxmb134e3sqpr" path="res://src/ui_parts/display.gd" id="1_oib5g"] [ext_resource type="Texture2D" uid="uid://iglrqrqyg4kn" path="res://assets/icons/Reference.svg" id="4_2hiq7"] @@ -6,20 +6,12 @@ [ext_resource type="Script" uid="uid://ynx3s1jc6bwq" path="res://src/ui_widgets/BetterButton.gd" id="6_3v3ve"] [ext_resource type="PackedScene" uid="uid://dad7fkhmsooc6" path="res://src/ui_widgets/number_edit.tscn" id="7_wrrfr"] [ext_resource type="Texture2D" uid="uid://dgoy38n5me7r0" path="res://assets/icons/Grid.svg" id="8_rll1m"] -[ext_resource type="PackedScene" uid="uid://oltvrf01xrxl" path="res://src/ui_widgets/zoom_menu.tscn" id="8_xtdmn"] -[ext_resource type="Script" uid="uid://b6pmlbnl76wmm" path="res://src/ui_parts/viewport.gd" id="9_4xrk7"] -[ext_resource type="Script" uid="uid://cbajqkgudfvh0" path="res://src/ui_parts/viewport_container.gd" id="9_ryr8t"] +[ext_resource type="PackedScene" uid="uid://ywqwxvmepuji" path="res://src/ui_widgets/zoom_widget.tscn" id="8_xtdmn"] +[ext_resource type="Script" uid="uid://1da5k40m2tl2" path="res://src/ui_parts/main_canvas.gd" id="9_ryr8t"] [ext_resource type="FontVariation" uid="uid://dg5vsg45k8y7w" path="res://assets/fonts/IconButtonFont.tres" id="10_kq8gf"] [ext_resource type="Texture2D" uid="uid://b7r171i82g3xl" path="res://assets/icons/Translate.svg" id="10_rll1m"] -[ext_resource type="Shader" uid="uid://i2y5pyhcgra2" path="res://src/shaders/zoom_shader.gdshader" id="10_x7ybk"] -[ext_resource type="Texture2D" uid="uid://c68og6bsqt0lb" path="res://assets/icons/backgrounds/Checkerboard.svg" id="11_1bm1s"] -[ext_resource type="Script" uid="uid://dtplje5mhdmrj" path="res://src/ui_parts/display_texture.gd" id="12_qi23s"] -[ext_resource type="Script" uid="uid://csqewpxr21ywy" path="res://src/ui_parts/handles_manager.gd" id="13_lwhwy"] [ext_resource type="Texture2D" uid="uid://d04w7h3k83w8u" path="res://assets/icons/element/svg.svg" id="15_kq8gf"] - -[sub_resource type="ShaderMaterial" id="ShaderMaterial_kqplg"] -shader = ExtResource("10_x7ybk") -shader_parameter/uv_scale = 1.0 +[ext_resource type="PackedScene" uid="uid://vrmbmt18wocm" path="res://src/ui_parts/debug_content.tscn" id="15_ryr8t"] [node name="Display" type="VBoxContainer"] anchors_preset = 15 @@ -39,56 +31,11 @@ size_flags_vertical = 3 [node name="VBoxContainer" type="VBoxContainer" parent="ViewportPanel"] layout_mode = 2 -[node name="ViewportContainer" type="SubViewportContainer" parent="ViewportPanel/VBoxContainer"] -clip_contents = true +[node name="Canvas" type="SubViewportContainer" parent="ViewportPanel/VBoxContainer"] layout_mode = 2 size_flags_vertical = 3 -stretch = true script = ExtResource("9_ryr8t") -[node name="Viewport" type="SubViewport" parent="ViewportPanel/VBoxContainer/ViewportContainer"] -unique_name_in_owner = true -disable_3d = true -handle_input_locally = false -gui_snap_controls_to_pixels = false -size = Vector2i(480, 811) -size_2d_override_stretch = true -render_target_update_mode = 4 -script = ExtResource("9_4xrk7") - -[node name="ReferenceTexture" type="TextureRect" parent="ViewportPanel/VBoxContainer/ViewportContainer/Viewport"] -visible = false -anchors_preset = -1 -offset_right = 128.0 -offset_bottom = 128.0 -expand_mode = 1 -stretch_mode = 5 - -[node name="Checkerboard" type="TextureRect" parent="ViewportPanel/VBoxContainer/ViewportContainer/Viewport"] -texture_filter = 1 -material = SubResource("ShaderMaterial_kqplg") -clip_contents = true -texture = ExtResource("11_1bm1s") -expand_mode = 1 -stretch_mode = 1 - -[node name="DisplayTexture" type="TextureRect" parent="ViewportPanel/VBoxContainer/ViewportContainer/Viewport/Checkerboard"] -clip_contents = true -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -expand_mode = 1 -script = ExtResource("12_qi23s") - -[node name="Controls" type="Control" parent="ViewportPanel/VBoxContainer/ViewportContainer/Viewport"] -layout_mode = 3 -anchors_preset = 0 -mouse_filter = 1 -script = ExtResource("13_lwhwy") - [node name="Toolbar" type="PanelContainer" parent="ViewportPanel/VBoxContainer"] layout_mode = 2 @@ -181,42 +128,7 @@ layout_mode = 2 size_flags_vertical = 4 alignment = 2 -[node name="DebugMargins" type="MarginContainer" parent="ViewportPanel"] -visible = false -layout_mode = 2 -size_flags_horizontal = 8 -size_flags_vertical = 0 -mouse_filter = 2 -theme_override_constants/margin_top = 36 -theme_override_constants/margin_right = 10 - -[node name="DebugContainer" type="VBoxContainer" parent="ViewportPanel/DebugMargins"] -unique_name_in_owner = true +[node name="DebugContent" parent="ViewportPanel" instance=ExtResource("15_ryr8t")] layout_mode = 2 -mouse_filter = 2 -theme_override_constants/separation = -18 -[node name="DebugLabel" type="Label" parent="ViewportPanel/DebugMargins/DebugContainer"] -layout_mode = 2 -theme_override_colors/font_color = Color(1, 1, 1, 1) -theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/line_spacing = 0 -theme_override_constants/outline_size = 4 -theme_override_font_sizes/font_size = 14 -horizontal_alignment = 2 - -[node name="InputDebugLabel" type="Label" parent="ViewportPanel/DebugMargins/DebugContainer"] -layout_mode = 2 -theme_override_colors/font_color = Color(0.75, 0.75, 0.75, 1) -theme_override_colors/font_outline_color = Color(0, 0, 0, 1) -theme_override_constants/line_spacing = 0 -theme_override_constants/outline_size = 4 -theme_override_font_sizes/font_size = 14 -horizontal_alignment = 2 - -[connection signal="size_changed" from="ViewportPanel/VBoxContainer/ViewportContainer/Viewport" to="ViewportPanel/VBoxContainer/ViewportContainer/Viewport" method="_on_size_changed"] [connection signal="pressed" from="ViewportPanel/VBoxContainer/Toolbar/ViewportOptions/Reference" to="." method="_on_reference_pressed"] -[connection signal="toggled" from="ViewportPanel/VBoxContainer/Toolbar/ViewportOptions/Snapping/SnapButton" to="." method="_on_snap_button_toggled"] -[connection signal="value_changed" from="ViewportPanel/VBoxContainer/Toolbar/ViewportOptions/Snapping/SnapNumberEdit" to="." method="_on_snap_number_edit_value_changed"] -[connection signal="zoom_changed" from="ViewportPanel/VBoxContainer/Toolbar/ViewportOptions/ZoomMenu" to="ViewportPanel/VBoxContainer/ViewportContainer/Viewport" method="_on_zoom_changed"] -[connection signal="zoom_reset_pressed" from="ViewportPanel/VBoxContainer/Toolbar/ViewportOptions/ZoomMenu" to="ViewportPanel/VBoxContainer/ViewportContainer/Viewport" method="center_frame"] diff --git a/src/ui_parts/display_texture.gd b/src/ui_parts/display_texture.gd deleted file mode 100644 index 2649c59..0000000 --- a/src/ui_parts/display_texture.gd +++ /dev/null @@ -1,49 +0,0 @@ -extends TextureRect - -var view_rect := Rect2(): - set(new_value): - if view_rect != new_value: - view_rect = new_value - queue_update() - -var _update_pending := false - -func _ready() -> void: - State.svg_changed.connect(queue_update) - State.zoom_changed.connect(queue_update) - State.view_rasterized_changed.connect(_on_view_rasterized_changed) - queue_update() - -func _on_view_rasterized_changed() -> void: - if State.zoom != 1.0: - queue_update() - - -func queue_update() -> void: - _update.call_deferred() - _update_pending = true - -func _update() -> void: - if not _update_pending: - return - - _update_pending = false - - var image_zoom := 1.0 if State.view_rasterized and State.zoom > 1.0 else State.zoom - var pixel_size := 1 / image_zoom - - # Translate to canvas coords. - var display_rect := view_rect.grow(pixel_size * 2) - display_rect.position = display_rect.position.snapped(Vector2(pixel_size, pixel_size)) - display_rect.position.x = maxf(display_rect.position.x, 0.0) - display_rect.position.y = maxf(display_rect.position.y, 0.0) - display_rect.size = display_rect.size.snapped(Vector2(pixel_size, pixel_size)) - display_rect.end.x = minf(display_rect.end.x, ceili(State.root_element.width)) - display_rect.end.y = minf(display_rect.end.y, ceili(State.root_element.height)) - - var svg_text := SVGParser.root_cutout_to_text(State.root_element, display_rect.size.x, - display_rect.size.y, Rect2(State.root_element.world_to_canvas(display_rect.position), - display_rect.size / State.root_element.canvas_transform.get_scale())) - Utils.set_control_position_fixed(self, display_rect.position) - set_deferred("size", display_rect.size) - texture = SVGTexture.create_from_string(svg_text, image_zoom) diff --git a/src/ui_parts/display_texture.gd.uid b/src/ui_parts/display_texture.gd.uid deleted file mode 100644 index f7203e0..0000000 --- a/src/ui_parts/display_texture.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dtplje5mhdmrj diff --git a/src/ui_parts/editor_scene.gd b/src/ui_parts/editor_scene.gd index 7383587..5f516cb 100644 --- a/src/ui_parts/editor_scene.gd +++ b/src/ui_parts/editor_scene.gd @@ -12,39 +12,12 @@ var main_splitter: SplitContainer func _ready() -> void: var shortcuts := ShortcutsRegistration.new() - shortcuts.add_shortcut("view_show_grid", State.toggle_show_grid, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) - shortcuts.add_shortcut("view_show_handles", State.toggle_show_handles, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) - shortcuts.add_shortcut("view_rasterized_svg", State.toggle_view_rasterized, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) - shortcuts.add_shortcut("view_show_reference", State.toggle_show_reference, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) - shortcuts.add_shortcut("view_overlay_reference", State.toggle_overlay_reference, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) - shortcuts.add_shortcut("load_reference", FileUtils.open_image_import_dialog, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("toggle_snap", func() -> void: Configs.savedata.snap *= -1, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) shortcuts.add_shortcut("import", FileUtils.open_svg_import_dialog, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) shortcuts.add_shortcut("export", HandlerGUI.open_export, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) shortcuts.add_shortcut("save", FileUtils.save_svg, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) shortcuts.add_shortcut("save_as", FileUtils.save_svg_as, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_tab", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index()), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_all_other_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.ALL_OTHERS), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_tabs_to_left", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.TO_LEFT), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_tabs_to_right", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.TO_RIGHT), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_empty_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.EMPTY), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("close_saved_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.SAVED), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("new_tab", Configs.savedata.add_empty_tab, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("select_next_tab", - func() -> void: Configs.savedata.set_active_tab_index(posmod(Configs.savedata.get_active_tab_index() + 1, Configs.savedata.get_tab_count())), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) - shortcuts.add_shortcut("select_previous_tab", - func() -> void: Configs.savedata.set_active_tab_index(posmod(Configs.savedata.get_active_tab_index() - 1, Configs.savedata.get_tab_count())), - ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) shortcuts.add_shortcut("optimize", State.optimize, ShortcutsRegistration.Behavior.STRICT_NO_PASSTHROUGH) shortcuts.add_shortcut("reset_svg", FileUtils.reset_svg, ShortcutsRegistration.Behavior.STRICT_NO_PASSTHROUGH) - shortcuts.add_shortcut("debug", State.toggle_show_debug) shortcuts.add_shortcut("ui_undo", func() -> void: Configs.savedata.get_active_tab().undo(), ShortcutsRegistration.Behavior.STRICT_NO_PASSTHROUGH) shortcuts.add_shortcut("ui_redo", func() -> void: Configs.savedata.get_active_tab().redo(), ShortcutsRegistration.Behavior.STRICT_NO_PASSTHROUGH) shortcuts.add_shortcut("ui_cancel", State.clear_all_selections, ShortcutsRegistration.Behavior.STRICT_NO_PASSTHROUGH) diff --git a/src/ui_parts/element_container.gd b/src/ui_parts/element_container.gd index 8e89568..fc21fe2 100644 --- a/src/ui_parts/element_container.gd +++ b/src/ui_parts/element_container.gd @@ -69,7 +69,7 @@ func update_proposed_xid() -> void: State.set_proposed_drop_xid(XIDUtils.get_parent_xid(next_xid) + PackedInt32Array([next_xid[-1] + 1])) elif next_xid[0] >= State.root_element.get_child_count(): State.set_proposed_drop_xid(next_xid) - elif XIDUtils.is_parent_or_self(prev_xid, next_xid): + elif XIDUtils.is_ancestor_or_self(prev_xid, next_xid): for i in range(prev_xid.size(), next_xid.size()): if next_xid[i] != 0: return diff --git a/src/ui_parts/export_menu.gd b/src/ui_parts/export_menu.gd index c12826b..d39335c 100644 --- a/src/ui_parts/export_menu.gd +++ b/src/ui_parts/export_menu.gd @@ -111,24 +111,23 @@ func _on_scale_edit_value_changed(new_value: float) -> void: undo_redo.add_undo_property(export_data, "upscale_amount", current_upscale_amount) undo_redo.commit_action() + func _on_width_edit_value_changed(new_value: float) -> void: - if roundi(dimensions.x * export_data.upscale_amount) == roundi(new_value): - return - var current_upscale_amount := export_data.upscale_amount - undo_redo.create_action() - undo_redo.add_do_property(export_data, "upscale_amount", new_value / dimensions.x) - undo_redo.add_undo_property(export_data, "upscale_amount", current_upscale_amount) - undo_redo.commit_action() + _dimension_component_change_common_logic(0, new_value) func _on_height_edit_value_changed(new_value: float) -> void: - if roundi(dimensions.y * export_data.upscale_amount) == roundi(new_value): + _dimension_component_change_common_logic(1, new_value) + +func _dimension_component_change_common_logic(component_index: int, new_value: float) -> void: + if roundi(dimensions[component_index] * export_data.upscale_amount) == roundi(new_value): return var current_upscale_amount := export_data.upscale_amount undo_redo.create_action() - undo_redo.add_do_property(export_data, "upscale_amount", new_value / dimensions.y) + undo_redo.add_do_property(export_data, "upscale_amount", new_value / dimensions[component_index]) undo_redo.add_undo_property(export_data, "upscale_amount", current_upscale_amount) undo_redo.commit_action() + # Everything gets updated at once when export config changes for simplicity. func update() -> void: # Determine which fields are visible. diff --git a/src/ui_parts/good_file_dialog.gd b/src/ui_parts/good_file_dialog.gd index f01be83..1ed6445 100644 --- a/src/ui_parts/good_file_dialog.gd +++ b/src/ui_parts/good_file_dialog.gd @@ -12,9 +12,8 @@ const folder_icon = preload("res://assets/icons/Folder.svg") const broken_file_icon = preload("res://assets/icons/FileBroken.svg") const text_file_icon = preload("res://assets/icons/TextFile.svg") -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] +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, MULTI_SELECT, SAVE} var mode: FileMode @@ -62,8 +61,7 @@ class Actions: right_click_callback = on_right_click -# Queueing is necessary for this one because in Godot, "Enter" is hard-coded -# to activate the selected items. +# 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: var actual_activation_callback := func() -> void: @@ -104,15 +102,28 @@ func _ready() -> void: HandlerGUI.register_shortcuts(self, shortcuts) # Signal connections. + close_button.pressed.connect(queue_free) + files_selected.connect(queue_free.unbind(1)) + file_field.text_changed.connect(_on_file_field_text_changed) + file_field.text_change_canceled.connect(_on_file_field_text_change_canceled) + file_field.text_submitted.connect(_on_file_field_text_submitted) folder_up_button.pressed.connect(_on_folder_up_button_pressed) + file_list.empty_clicked.connect(_on_file_list_empty_clicked) + file_list.item_activated.connect(_on_file_list_item_activated) + file_list.item_clicked.connect(_on_file_list_item_clicked) file_list.item_selected.connect(_on_file_list_item_selected) file_list.multi_selected.connect(_on_file_list_item_multi_selected) + path_field.text_submitted.connect(_on_path_field_text_submitted) + show_hidden_button.toggled.connect(_on_show_hidden_button_toggled) + search_button.toggled.connect(_on_search_button_toggled) + drives_list.item_selected.connect(_on_drives_list_item_selected) + search_field.text_changed.connect(_on_search_field_text_changed) + search_field.text_change_canceled.connect(_on_search_field_text_change_canceled) refresh_button.pressed.connect(refresh_dir) - close_button.pressed.connect(queue_free) - 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.SAVE: file_container.hide() @@ -135,8 +146,7 @@ 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( - mode == FileMode.MULTI_SELECT, 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("Save") if mode == FileMode.SAVE else Translator.translate("Select") @@ -254,7 +264,7 @@ func _setup_file_images() -> void: var visible_end := visible_start + file_list.size.y for item_idx in file_list.item_count: var file_rect := file_list.get_item_rect(item_idx) - if !is_instance_valid(file_list.get_item_icon(item_idx)) and file_rect.end.y > visible_start and file_rect.position.y < visible_end: + if not is_instance_valid(file_list.get_item_icon(item_idx)) and file_rect.end.y > visible_start and file_rect.position.y < visible_end: var file := file_list.get_item_text(item_idx) match file.get_extension(): "xml": @@ -264,14 +274,14 @@ func _setup_file_images() -> void: var svg_text := FileAccess.get_file_as_string(current_dir.path_join(file)) var img := Image.new() img.load_svg_from_string(svg_text) - if !is_instance_valid(img) or img.is_empty(): + if not is_instance_valid(img) or img.is_empty(): file_list.set_item_icon(item_idx, broken_file_icon) else: var svg_texture := SVGTexture.create_from_string(svg_text, minf(item_height / img.get_width(), item_height / img.get_height())) file_list.set_item_icon(item_idx, svg_texture) _: var img := Image.load_from_file(current_dir.path_join(file)) - if !is_instance_valid(img) or img.is_empty(): + if not is_instance_valid(img) or img.is_empty(): file_list.set_item_icon(item_idx, broken_file_icon) else: file_list.set_item_icon(item_idx, ImageTexture.create_from_image(img)) @@ -343,7 +353,7 @@ func _create_folder_error(text: String) -> String: func _on_create_folder_finished(text: String) -> void: dir_cursor = DirAccess.open(current_dir) - if !is_instance_valid(dir_cursor): + if not is_instance_valid(dir_cursor): return var err := dir_cursor.make_dir(text) diff --git a/src/ui_parts/good_file_dialog.tscn b/src/ui_parts/good_file_dialog.tscn index 09c59ba..14d6f09 100644 --- a/src/ui_parts/good_file_dialog.tscn +++ b/src/ui_parts/good_file_dialog.tscn @@ -135,15 +135,3 @@ layout_mode = 2 size_flags_horizontal = 6 focus_mode = 0 mouse_default_cursor_shape = 2 - -[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"] -[connection signal="item_selected" from="VBoxContainer/MainContainer/DrivesContainer/DrivesList" to="." method="_on_drives_list_item_selected"] -[connection signal="text_change_canceled" from="VBoxContainer/MainContainer/FilePicker/SearchField" to="." method="_on_search_field_text_change_canceled"] -[connection signal="text_changed" from="VBoxContainer/MainContainer/FilePicker/SearchField" to="." method="_on_search_field_text_changed"] -[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="text_change_canceled" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_change_canceled"] -[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.uid b/src/ui_parts/handles_manager.gd.uid deleted file mode 100644 index bb4589a..0000000 --- a/src/ui_parts/handles_manager.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://csqewpxr21ywy diff --git a/src/ui_parts/import_warning_menu.gd b/src/ui_parts/import_warning_menu.gd index 4e9db5c..0b7b030 100644 --- a/src/ui_parts/import_warning_menu.gd +++ b/src/ui_parts/import_warning_menu.gd @@ -28,28 +28,26 @@ func _ready() -> void: cancel_button.pressed.connect(queue_free) # Convert forward and backward to show how VectorTouch would display the given SVG. - var imported_text_parse_result := SVGParser.text_to_root(imported_text) + var imported_text_parse_result := SVGParser.markup_to_root(imported_text) if is_instance_valid(imported_text_parse_result.svg): - var preview_text := SVGParser.root_to_editor_text(imported_text_parse_result.svg) - var preview_parse_result := SVGParser.text_to_root(preview_text) + var preview_text := SVGParser.root_to_editor_markup(imported_text_parse_result.svg) + var preview_parse_result := SVGParser.markup_to_root(preview_text) var preview := preview_parse_result.svg if is_instance_valid(preview): - texture_preview.setup_svg(SVGParser.root_to_editor_text(preview), preview.get_size()) + texture_preview.setup_svg(SVGParser.root_to_editor_markup(preview), preview.get_size()) if imported_text_parse_result.error != SVGParser.ParseError.OK: texture_preview.hide() margin_container.custom_minimum_size.y = 48 size.y = 0 warnings_label.add_theme_color_override("default_color", Configs.savedata.basic_color_error) - warnings_label.text = "[center]%s: %s" % [Translator.translate( - "Syntax error"), SVGParser.get_error_string(imported_text_parse_result.error)] + warnings_label.text = "[center]%s: %s" % [Translator.translate("Syntax error"), SVGParser.get_parsing_error_string(imported_text_parse_result.error)] else: var svg_warnings := get_svg_warnings(imported_text_parse_result.svg) if svg_warnings.is_empty(): finish_import() else: - warnings_label.add_theme_color_override("default_color", - Configs.savedata.basic_color_warning) + warnings_label.add_theme_color_override("default_color", Configs.savedata.basic_color_warning) for warning in svg_warnings: warnings_label.text += warning + "\n" ok_button.grab_focus() diff --git a/src/ui_parts/mac_menu.gd b/src/ui_parts/mac_menu.gd index 77a9ebd..53816c9 100644 --- a/src/ui_parts/mac_menu.gd +++ b/src/ui_parts/mac_menu.gd @@ -1,216 +1,211 @@ # The MacOS-specific top menu. extends Node -var global_rid: RID -var appl_rid: RID -var help_rid: RID - -var file_rid: RID -var file_idx: int -var file_optimize_idx: int -var file_reset_svg_idx: int - -var edit_rid: RID -var edit_idx: int -var tool_rid: RID -var tool_idx: int - -var view_rid: RID -var view_idx: int -var view_show_grid_idx: int -var view_show_handles_idx: int -var view_rasterized_svg_idx: int -var view_show_reference_idx: int -var view_overlay_reference_idx: int -var view_show_debug_idx: int - -var snap_rid: RID -var snap_idx: int -var snap_enable_idx: int -var snap_0125_idx: int -var snap_025_idx: int -var snap_05_idx: int -var snap_1_idx: int -var snap_2_idx: int -var snap_4_idx: int - - -func _enter_tree() -> void: - # Included menus - global_rid = NativeMenu.get_system_menu(NativeMenu.MAIN_MENU_ID) - appl_rid = NativeMenu.get_system_menu(NativeMenu.APPLICATION_MENU_ID) - help_rid = NativeMenu.get_system_menu(NativeMenu.HELP_MENU_ID) - # Custom menus - _generate_main_menus() - _setup_menu_items() - # Updates - Configs.language_changed.connect(_reset_menus) - Configs.shortcuts_changed.connect(_reset_menus) - # For now only keep check items up to date. Disabling things reliably is complicated. - Configs.snap_changed.connect(_on_snap_changed) - State.view_rasterized_changed.connect(_on_view_rasterized_changed) - State.show_grid_changed.connect(_on_show_grid_changed) - State.show_handles_changed.connect(_on_show_handles_changed) - State.show_reference_changed.connect(_on_show_reference_changed) - State.overlay_reference_changed.connect(_on_overlay_reference_changed) - State.show_debug_changed.connect(_on_show_debug_changed) - # Updating checked items didn't work without the await. - await get_tree().process_frame - _on_snap_changed() - _on_view_rasterized_changed() - _on_show_grid_changed() - _on_show_handles_changed() - _on_show_reference_changed() - _on_overlay_reference_changed() - _on_show_debug_changed() - - -func _reset_menus() -> void: - NativeMenu.remove_item(global_rid, snap_idx) - NativeMenu.remove_item(global_rid, view_idx) - NativeMenu.remove_item(global_rid, tool_idx) - NativeMenu.remove_item(global_rid, edit_idx) - NativeMenu.remove_item(global_rid, file_idx) - NativeMenu.free_menu(file_rid) - NativeMenu.free_menu(edit_rid) - NativeMenu.free_menu(tool_rid) - NativeMenu.free_menu(view_rid) - NativeMenu.free_menu(snap_rid) - _generate_main_menus() - _setup_menu_items() - - -func _generate_main_menus() -> void: - file_rid = NativeMenu.create_menu() - edit_rid = NativeMenu.create_menu() - tool_rid = NativeMenu.create_menu() - view_rid = NativeMenu.create_menu() - snap_rid = NativeMenu.create_menu() - file_idx = NativeMenu.add_submenu_item(global_rid, - Translator.translate("File"), file_rid) - edit_idx = NativeMenu.add_submenu_item(global_rid, - Translator.translate("Edit"), edit_rid) - tool_idx = NativeMenu.add_submenu_item(global_rid, - Translator.translate("Tool"), tool_rid) - view_idx = NativeMenu.add_submenu_item(global_rid, - Translator.translate("View"), view_rid) - snap_idx = NativeMenu.add_submenu_item(global_rid, - Translator.translate("Snap"), snap_rid) - - -func _clear_menu_items() -> void: - NativeMenu.clear(appl_rid) - NativeMenu.clear(help_rid) - NativeMenu.clear(file_rid) - NativeMenu.clear(edit_rid) - NativeMenu.clear(tool_rid) - NativeMenu.clear(view_rid) - NativeMenu.clear(snap_rid) - - -func _setup_menu_items() -> void: - _clear_menu_items() - _add_item(appl_rid, "open_settings") - # Help menu. - _add_icon_item(help_rid, "open_settings") - _add_icon_item(help_rid, "about_repo") - _add_icon_item(help_rid, "about_info") - _add_icon_item(help_rid, "about_donate") - _add_icon_item(help_rid, "about_website") - _add_icon_item(help_rid, "check_updates") - # File menu. - _add_many_items(file_rid, PackedStringArray(["import", "export", "save", "save_as"])) - NativeMenu.add_separator(file_rid) - _add_item(file_rid, "copy_svg_text") - file_optimize_idx = _add_item(file_rid, "optimize") - NativeMenu.add_separator(file_rid) - file_reset_svg_idx = _add_item(file_rid, "reset_svg") - # Edit and Tool menus. - _add_many_items(edit_rid, ShortcutUtils.get_actions("edit")) - _add_many_items(tool_rid, ShortcutUtils.get_actions("tool")) - # View menu. - view_show_grid_idx = _add_check_item(view_rid, "view_show_grid") - view_show_handles_idx = _add_check_item(view_rid, "view_show_handles") - view_rasterized_svg_idx = _add_check_item(view_rid, "view_rasterized_svg") - NativeMenu.add_separator(view_rid) - view_show_reference_idx = _add_item(view_rid, "load_reference") - view_show_reference_idx = _add_check_item(view_rid, "view_show_reference") - view_overlay_reference_idx = _add_check_item(view_rid, "view_overlay_reference") - view_show_debug_idx = _add_check_item(view_rid, "view_show_debug") - NativeMenu.add_separator(view_rid) - _add_many_items(view_rid, PackedStringArray(["zoom_in", "zoom_out", "zoom_reset"])) - # Snap menu. - snap_enable_idx = _add_check_item(snap_rid, "toggle_snap") - NativeMenu.add_separator(snap_rid) - snap_0125_idx = NativeMenu.add_radio_check_item(snap_rid, "0.125", _set_snap, _set_snap, 0.125) - snap_025_idx = NativeMenu.add_radio_check_item(snap_rid, "0.25", _set_snap, _set_snap, 0.25) - snap_05_idx = NativeMenu.add_radio_check_item(snap_rid, "0.5", _set_snap, _set_snap, 0.5) - snap_1_idx = NativeMenu.add_radio_check_item(snap_rid, "1", _set_snap, _set_snap, 1) - snap_2_idx = NativeMenu.add_radio_check_item(snap_rid, "2", _set_snap, _set_snap, 2) - snap_4_idx = NativeMenu.add_radio_check_item(snap_rid, "4", _set_snap, _set_snap, 4) - - -func _add_item(menu_rid: RID, action_name: String) -> int: - return NativeMenu.add_item(menu_rid, - TranslationUtils.get_action_description(action_name), - HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name, - _get_action_keycode(action_name)) - -func _add_many_items(menu_rid: RID, actions: PackedStringArray) -> void: - for action in actions: - _add_item(menu_rid, action) - -func _add_check_item(menu_rid: RID, action_name: String) -> int: - return NativeMenu.add_check_item(menu_rid, - TranslationUtils.get_action_description(action_name), - HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name) - -func _add_many_icon_items(menu_rid: RID, actions: PackedStringArray) -> void: - for action in actions: - _add_icon_item(menu_rid, action) - -func _add_icon_item(menu_rid: RID, action_name: String) -> int: - return NativeMenu.add_icon_item(menu_rid, - ShortcutUtils.get_action_icon(action_name), - TranslationUtils.get_action_description(action_name), - HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name) - - -func _get_action_keycode(action: String) -> Key: - var shortcut := ShortcutUtils.get_action_first_valid_shortcut(action) - if is_instance_valid(shortcut): - return shortcut.get_keycode_with_modifiers() - return KEY_NONE - - -func _on_view_rasterized_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_rasterized_svg_idx, State.view_rasterized) - -func _on_show_grid_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_show_grid_idx, State.show_grid) - -func _on_show_handles_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_show_handles_idx, State.show_handles) - -func _on_show_reference_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_show_reference_idx, State.show_reference) - -func _on_overlay_reference_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_overlay_reference_idx, State.overlay_reference) - -func _on_show_debug_changed() -> void: - NativeMenu.set_item_checked(view_rid, view_show_debug_idx, State.show_debug) - - -func _on_snap_changed() -> void: - var snap_amount := absf(Configs.savedata.snap) - NativeMenu.set_item_checked(snap_rid, snap_enable_idx, Configs.savedata.snap > 0) - NativeMenu.set_item_checked(snap_rid, snap_0125_idx, is_equal_approx(snap_amount, 0.125)) - NativeMenu.set_item_checked(snap_rid, snap_025_idx, is_equal_approx(snap_amount, 0.25)) - NativeMenu.set_item_checked(snap_rid, snap_05_idx, is_equal_approx(snap_amount, 0.5)) - NativeMenu.set_item_checked(snap_rid, snap_1_idx, is_equal_approx(snap_amount, 1)) - NativeMenu.set_item_checked(snap_rid, snap_2_idx, is_equal_approx(snap_amount, 2)) - NativeMenu.set_item_checked(snap_rid, snap_4_idx, is_equal_approx(snap_amount, 4)) - -func _set_snap(amount: float) -> void: - Configs.savedata.snap = amount +# TODO This made things really complicated during a refactor, it's to be reinstated in the future. + +#var global_rid: RID +#var appl_rid: RID +#var help_rid: RID +# +#var file_rid: RID +#var file_idx: int +#var file_optimize_idx: int +#var file_reset_svg_idx: int +# +#var edit_rid: RID +#var edit_idx: int +#var tool_rid: RID +#var tool_idx: int +# +#var view_rid: RID +#var view_idx: int +#var view_show_grid_idx: int +#var view_show_handles_idx: int +#var view_rasterized_svg_idx: int +#var view_show_reference_idx: int +#var view_overlay_reference_idx: int +#var view_show_debug_idx: int +# +#var snap_rid: RID +#var snap_idx: int +#var snap_enable_idx: int +#var snap_0125_idx: int +#var snap_025_idx: int +#var snap_05_idx: int +#var snap_1_idx: int +#var snap_2_idx: int +#var snap_4_idx: int +# +# +#func _enter_tree() -> void: + ## Included menus + #global_rid = NativeMenu.get_system_menu(NativeMenu.MAIN_MENU_ID) + #appl_rid = NativeMenu.get_system_menu(NativeMenu.APPLICATION_MENU_ID) + #help_rid = NativeMenu.get_system_menu(NativeMenu.HELP_MENU_ID) + ## Custom menus + #_generate_main_menus() + #_setup_menu_items() + ## Updates + #Configs.language_changed.connect(_reset_menus) + #Configs.shortcuts_changed.connect(_reset_menus) + ## For now only keep check items up to date. Disabling things reliably is complicated. + #Configs.snap_changed.connect(_on_snap_changed) + #State.view_rasterized_changed.connect(_on_view_rasterized_changed) + #State.show_grid_changed.connect(_on_show_grid_changed) + #State.show_handles_changed.connect(_on_show_handles_changed) + #Configs.active_tab_reference_changed.connect(_on_active_tab_reference_changed) + ## Updating checked items didn't work without the await. + #await get_tree().process_frame + #_on_snap_changed() + #_on_view_rasterized_changed() + #_on_show_grid_changed() + #_on_show_handles_changed() + #_on_active_tab_reference_changed() +# +# +#func _reset_menus() -> void: + #NativeMenu.remove_item(global_rid, snap_idx) + #NativeMenu.remove_item(global_rid, view_idx) + #NativeMenu.remove_item(global_rid, tool_idx) + #NativeMenu.remove_item(global_rid, edit_idx) + #NativeMenu.remove_item(global_rid, file_idx) + #NativeMenu.free_menu(file_rid) + #NativeMenu.free_menu(edit_rid) + #NativeMenu.free_menu(tool_rid) + #NativeMenu.free_menu(view_rid) + #NativeMenu.free_menu(snap_rid) + #_generate_main_menus() + #_setup_menu_items() +# +# +#func _generate_main_menus() -> void: + #file_rid = NativeMenu.create_menu() + #edit_rid = NativeMenu.create_menu() + #tool_rid = NativeMenu.create_menu() + #view_rid = NativeMenu.create_menu() + #snap_rid = NativeMenu.create_menu() + #file_idx = NativeMenu.add_submenu_item(global_rid, + #Translator.translate("File"), file_rid) + #edit_idx = NativeMenu.add_submenu_item(global_rid, + #Translator.translate("Edit"), edit_rid) + #tool_idx = NativeMenu.add_submenu_item(global_rid, + #Translator.translate("Tool"), tool_rid) + #view_idx = NativeMenu.add_submenu_item(global_rid, + #Translator.translate("View"), view_rid) + #snap_idx = NativeMenu.add_submenu_item(global_rid, + #Translator.translate("Snap"), snap_rid) +# +# +#func _clear_menu_items() -> void: + #NativeMenu.clear(appl_rid) + #NativeMenu.clear(help_rid) + #NativeMenu.clear(file_rid) + #NativeMenu.clear(edit_rid) + #NativeMenu.clear(tool_rid) + #NativeMenu.clear(view_rid) + #NativeMenu.clear(snap_rid) +# +# +#func _setup_menu_items() -> void: + #_clear_menu_items() + #_add_item(appl_rid, "open_settings") + ## Help menu. + #_add_icon_item(help_rid, "open_settings") + #_add_icon_item(help_rid, "about_repo") + #_add_icon_item(help_rid, "about_info") + #_add_icon_item(help_rid, "about_donate") + #_add_icon_item(help_rid, "about_website") + #_add_icon_item(help_rid, "check_updates") + ## File menu. + #_add_many_items(file_rid, PackedStringArray(["import", "export", "save", "save_as"])) + #NativeMenu.add_separator(file_rid) + #_add_item(file_rid, "copy_svg_text") + #file_optimize_idx = _add_item(file_rid, "optimize") + #NativeMenu.add_separator(file_rid) + #file_reset_svg_idx = _add_item(file_rid, "reset_svg") + ## Edit and Tool menus. + #_add_many_items(edit_rid, ShortcutUtils.get_actions("edit")) + #_add_many_items(tool_rid, ShortcutUtils.get_actions("tool")) + ## View menu. + #view_show_grid_idx = _add_check_item(view_rid, "view_show_grid") + #view_show_handles_idx = _add_check_item(view_rid, "view_show_handles") + #view_rasterized_svg_idx = _add_check_item(view_rid, "view_rasterized_svg") + #NativeMenu.add_separator(view_rid) + #view_show_reference_idx = _add_item(view_rid, "load_reference") + #view_show_reference_idx = _add_check_item(view_rid, "view_show_reference") + #view_overlay_reference_idx = _add_check_item(view_rid, "view_overlay_reference") + #view_show_debug_idx = _add_check_item(view_rid, "view_show_debug") + #NativeMenu.add_separator(view_rid) + #_add_many_items(view_rid, PackedStringArray(["zoom_in", "zoom_out", "zoom_reset"])) + ## Snap menu. + #snap_enable_idx = _add_check_item(snap_rid, "toggle_snap") + #NativeMenu.add_separator(snap_rid) + #snap_0125_idx = NativeMenu.add_radio_check_item(snap_rid, "0.125", _set_snap, _set_snap, 0.125) + #snap_025_idx = NativeMenu.add_radio_check_item(snap_rid, "0.25", _set_snap, _set_snap, 0.25) + #snap_05_idx = NativeMenu.add_radio_check_item(snap_rid, "0.5", _set_snap, _set_snap, 0.5) + #snap_1_idx = NativeMenu.add_radio_check_item(snap_rid, "1", _set_snap, _set_snap, 1) + #snap_2_idx = NativeMenu.add_radio_check_item(snap_rid, "2", _set_snap, _set_snap, 2) + #snap_4_idx = NativeMenu.add_radio_check_item(snap_rid, "4", _set_snap, _set_snap, 4) +# +# +#func _add_item(menu_rid: RID, action_name: String) -> int: + #return NativeMenu.add_item(menu_rid, + #TranslationUtils.get_action_description(action_name), + #HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name, + #_get_action_keycode(action_name)) +# +#func _add_many_items(menu_rid: RID, actions: PackedStringArray) -> void: + #for action in actions: + #_add_item(menu_rid, action) +# +#func _add_check_item(menu_rid: RID, action_name: String) -> int: + #return NativeMenu.add_check_item(menu_rid, + #TranslationUtils.get_action_description(action_name), + #HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name) +# +#func _add_many_icon_items(menu_rid: RID, actions: PackedStringArray) -> void: + #for action in actions: + #_add_icon_item(menu_rid, action) +# +#func _add_icon_item(menu_rid: RID, action_name: String) -> int: + #return NativeMenu.add_icon_item(menu_rid, + #ShortcutUtils.get_action_icon(action_name), + #TranslationUtils.get_action_description(action_name), + #HandlerGUI.throw_action_event, HandlerGUI.throw_action_event, action_name) +# +# +#func _get_action_keycode(action: String) -> Key: + #var shortcut := ShortcutUtils.get_action_first_valid_shortcut(action) + #if is_instance_valid(shortcut): + #return shortcut.get_keycode_with_modifiers() + #return KEY_NONE +# +# +#func _on_view_rasterized_changed() -> void: + #NativeMenu.set_item_checked(view_rid, view_rasterized_svg_idx, State.view_rasterized) +# +#func _on_show_grid_changed() -> void: + #NativeMenu.set_item_checked(view_rid, view_show_grid_idx, State.show_grid) +# +#func _on_show_handles_changed() -> void: + #NativeMenu.set_item_checked(view_rid, view_show_handles_idx, State.show_handles) +# +#func _on_active_tab_reference_changed() -> void: + #var active_tab := Configs.savedata.get_active_tab() + #var has_reference := is_instance_valid(active_tab.reference_image) + #NativeMenu.set_item_checked(view_rid, view_show_reference_idx, has_reference and active_tab.show_reference) + #NativeMenu.set_item_checked(view_rid, view_overlay_reference_idx, has_reference and active_tab.overlay_reference) +# +# +#func _on_snap_changed() -> void: + #var snap_amount := absf(Configs.savedata.snap) + #NativeMenu.set_item_checked(snap_rid, snap_enable_idx, Configs.savedata.snap > 0) + #NativeMenu.set_item_checked(snap_rid, snap_0125_idx, is_equal_approx(snap_amount, 0.125)) + #NativeMenu.set_item_checked(snap_rid, snap_025_idx, is_equal_approx(snap_amount, 0.25)) + #NativeMenu.set_item_checked(snap_rid, snap_05_idx, is_equal_approx(snap_amount, 0.5)) + #NativeMenu.set_item_checked(snap_rid, snap_1_idx, is_equal_approx(snap_amount, 1)) + #NativeMenu.set_item_checked(snap_rid, snap_2_idx, is_equal_approx(snap_amount, 2)) + #NativeMenu.set_item_checked(snap_rid, snap_4_idx, is_equal_approx(snap_amount, 4)) +# +#func _set_snap(amount: float) -> void: + #Configs.savedata.snap = amount diff --git a/src/ui_parts/main_canvas.gd b/src/ui_parts/main_canvas.gd new file mode 100644 index 0000000..df18b9e --- /dev/null +++ b/src/ui_parts/main_canvas.gd @@ -0,0 +1,116 @@ +extends Canvas + +var reference_image: Texture2D: + set(new_value): + if reference_image != new_value: + reference_image = new_value + Configs.savedata.get_active_tab().reference_image = new_value + sync_reference_image() + +var show_reference := false: + set(new_value): + if show_reference != new_value: + show_reference = new_value + Configs.savedata.get_active_tab().show_reference = new_value + sync_reference_image() + +var overlay_reference := false: + set(new_value): + if overlay_reference != new_value: + overlay_reference = new_value + Configs.savedata.get_active_tab().overlay_reference = new_value + sync_reference_image() + + +var reference_texture_rect: TextureRect + +func _ready() -> void: + super() + camera_center_changed.connect(func() -> void: Configs.savedata.get_active_tab().camera_center = camera_center) + camera_zoom_changed.connect(func() -> void: Configs.savedata.get_active_tab().camera_zoom = camera_zoom) + var shortcuts := ShortcutsRegistration.new() + shortcuts.add_shortcut("view_show_grid", toggle_show_grid, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + shortcuts.add_shortcut("view_show_handles", toggle_show_handles, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + shortcuts.add_shortcut("view_rasterized_svg", toggle_view_rasterized, ShortcutsRegistration.Behavior.PASS_THROUGH_AND_PRESERVE_POPUPS) + HandlerGUI.register_shortcuts(self, shortcuts) + + State.svg_changed.connect(_on_svg_changed) + _on_svg_changed() + State.hover_changed.connect(_on_hover_changed) + State.selection_changed.connect(_on_selection_changed) + + Configs.active_tab_changed.connect(sync_reference_image) + Configs.active_tab_changed.connect(_on_svg_changed) + Configs.active_tab_changed.connect(sync_camera) + await get_tree().process_frame + center_frame() + +func _on_svg_resized() -> void: + root_element = State.root_element + sync_checkerboard() + center_frame() + queue_redraw() + +var _current_svg_size: Vector2 + +func _on_root_element_attribute_changed(attribute_name: String) -> void: + if attribute_name in ["width", "height", "viewBox"]: + _current_svg_size = root_element.get_size() + _on_svg_resized() + +func _on_svg_changed() -> void: + if root_element != State.root_element: + root_element = State.root_element + root_element.attribute_changed.connect(_on_root_element_attribute_changed) + _current_svg_size = root_element.get_size() + queue_texture_update(true) + handles_manager.queue_update_handles() + +func _on_hover_changed() -> void: + hovered_xid = State.hovered_xid + semi_hovered_xid = State.semi_hovered_xid + inner_hovered = State.inner_hovered + +func _on_selection_changed() -> void: + selected_xids = State.selected_xids + selection_pivot_xid = State.selection_pivot_xid + semi_selected_xid = State.semi_selected_xid + inner_selections = State.inner_selections + inner_selection_pivot = State.inner_selection_pivot + + +func zoom_in() -> void: + set_zoom(camera_zoom * sqrt(2)) + +func zoom_out() -> void: + set_zoom(camera_zoom / sqrt(2)) + +func sync_reference_image() -> void: + var active_tab := Configs.savedata.get_active_tab() + if is_instance_valid(active_tab.reference_image): + if not is_instance_valid(reference_texture_rect): + reference_texture_rect = TextureRect.new() + reference_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + reference_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + reference_texture_rect.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR + viewport.add_child(reference_texture_rect) + var _on_checkerboard_resized := func() -> void: + reference_texture_rect.size = checkerboard.size + checkerboard.resized.connect(_on_checkerboard_resized) + reference_texture_rect.tree_exited.connect(checkerboard.resized.disconnect.bind(_on_checkerboard_resized)) + _on_checkerboard_resized.call() + + reference_texture_rect.texture = active_tab.reference_image + reference_texture_rect.visible = active_tab.show_reference + viewport.move_child(reference_texture_rect, -1 if active_tab.overlay_reference else 0) + elif is_instance_valid(reference_texture_rect): + reference_texture_rect.queue_free() + +func sync_camera() -> void: + var active_tab := Configs.savedata.get_active_tab() + if active_tab.camera_center.is_finite() and active_tab.camera_zoom > 0: + camera_center = active_tab.camera_center + camera_zoom = active_tab.camera_zoom + adjust_view() + else: + center_frame() diff --git a/src/ui_parts/main_canvas.gd.uid b/src/ui_parts/main_canvas.gd.uid new file mode 100644 index 0000000..ca1cdad --- /dev/null +++ b/src/ui_parts/main_canvas.gd.uid @@ -0,0 +1 @@ +uid://1da5k40m2tl2 diff --git a/src/ui_parts/move_to_overlay.gd b/src/ui_parts/move_to_overlay.gd index 538abe1..47ef917 100644 --- a/src/ui_parts/move_to_overlay.gd +++ b/src/ui_parts/move_to_overlay.gd @@ -6,7 +6,7 @@ func _can_drop_data(_at_position: Vector2, data: Variant) -> bool: return false get_parent().update_proposed_xid() for xid in data: - if XIDUtils.is_parent(xid, State.proposed_drop_xid): + if XIDUtils.is_ancestor(xid, State.proposed_drop_xid): return false if State.proposed_drop_xid.is_empty(): return false diff --git a/src/ui_parts/settings_menu.gd b/src/ui_parts/settings_menu.gd index dcbec7e..319b658 100644 --- a/src/ui_parts/settings_menu.gd +++ b/src/ui_parts/settings_menu.gd @@ -35,7 +35,7 @@ func _ready() -> void: back_button.pressed.connect(queue_free) change_orientation() Configs.orientation_changed.connect(change_orientation) - + lang_button.pressed.connect(_on_language_pressed) scroll_container.get_v_scroll_bar().visibility_changed.connect(adjust_right_margin) adjust_right_margin() @@ -131,7 +131,8 @@ func setup_content() -> void: func set_preview(node: Control) -> void: for child in preview_panel.get_children(): child.queue_free() - preview_panel.add_child(node) + if is_instance_valid(node): + preview_panel.add_child(node) func _on_language_pressed() -> void: @@ -149,7 +150,7 @@ func _on_language_pressed() -> void: btn_arr.append(ContextPopup.create_button( TranslationUtils.get_locale_display(locale), _on_language_chosen.bind(locale), is_current_locale, - null, Utils.num_simple(translated_count * 100.0 / strings_count, 1) + "%")) + null, false, Utils.num_simple(translated_count * 100.0 / strings_count, 1) + "%")) else: btn_arr.append(ContextPopup.create_button( TranslationUtils.get_locale_display(locale), diff --git a/src/ui_parts/settings_menu.tscn b/src/ui_parts/settings_menu.tscn index ef7262b..01fdf01 100644 --- a/src/ui_parts/settings_menu.tscn +++ b/src/ui_parts/settings_menu.tscn @@ -73,5 +73,3 @@ theme_override_constants/margin_bottom = 4 custom_minimum_size = Vector2(0, 64) layout_mode = 2 theme_type_variation = &"TextBox" - -[connection signal="pressed" from="VBoxContainer/PanelContainer/Language" to="." method="_on_language_pressed"] diff --git a/src/ui_parts/tab_bar.gd b/src/ui_parts/tab_bar.gd index 1071d39..d7e502b 100644 --- a/src/ui_parts/tab_bar.gd +++ b/src/ui_parts/tab_bar.gd @@ -27,6 +27,28 @@ var proposed_drop_idx := -1: queue_redraw() func _ready() -> void: + var shortcuts := ShortcutsRegistration.new() + shortcuts.add_shortcut("close_tab", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index()), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("close_all_other_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.ALL_OTHERS), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("close_tabs_to_left", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.TO_LEFT), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("close_tabs_to_right", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.TO_RIGHT), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("close_empty_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.EMPTY), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("close_saved_tabs", func() -> void: FileUtils.close_tabs(Configs.savedata.get_active_tab_index(), FileUtils.TabCloseMode.SAVED), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("new_tab", Configs.savedata.add_empty_tab, ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("select_next_tab", + func() -> void: Configs.savedata.set_active_tab_index(posmod(Configs.savedata.get_active_tab_index() + 1, Configs.savedata.get_tab_count())), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + shortcuts.add_shortcut("select_previous_tab", + func() -> void: Configs.savedata.set_active_tab_index(posmod(Configs.savedata.get_active_tab_index() - 1, Configs.savedata.get_tab_count())), + ShortcutsRegistration.Behavior.PASS_THROUGH_POPUPS) + HandlerGUI.register_shortcuts(self, shortcuts) + Configs.active_tab_changed.connect(activate) Configs.tabs_changed.connect(activate) Configs.active_tab_changed.connect(scroll_to_active) @@ -62,7 +84,7 @@ func _draw() -> void: current_tab_name = "* " + current_tab_name if (has_transient_tab and drawing_transient_tab) or (not has_transient_tab and tab_index == Configs.savedata.get_active_tab_index()): - draw_style_box(get_theme_stylebox("tab_selected", "TabContainer"), rect) + get_theme_stylebox("tab_selected", "TabContainer").draw(ci, rect) var text_line_width := rect.size.x - size.y if text_line_width > 0: var text_line := TextLine.new() @@ -79,7 +101,7 @@ func _draw() -> void: else: var is_hovered := rect.has_point(mouse_pos) var tab_style := "tab_hovered" if is_hovered else "tab_unselected" - draw_style_box(get_theme_stylebox(tab_style, "TabContainer"), rect) + get_theme_stylebox(tab_style, "TabContainer").draw(ci, rect) var text_line_width := rect.size.x - 8 if text_line_width > 0: @@ -93,7 +115,7 @@ func _draw() -> void: var add_button_rect := get_add_button_rect() 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), + plus_icon.draw_rect(ci, Rect2(add_button_rect.position + (add_button_rect.size - plus_icon_size) / 2.0, plus_icon_size), false, ThemeUtils.tinted_contrast_color) var scroll_backwards_rect := get_scroll_backwards_area_rect() @@ -119,8 +141,7 @@ func _draw() -> void: icon_modulate = get_theme_color("icon_disabled_color", "Button") else: var line_x := scroll_forwards_rect.position.x - draw_line(Vector2(line_x, 0), Vector2(line_x, size.y), - ThemeUtils.basic_panel_border_color) + draw_line(Vector2(line_x, 0), Vector2(line_x, size.y), ThemeUtils.basic_panel_border_color) if scroll_forwards_rect.has_point(mouse_pos): var stylebox_theme := "pressed" if Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) else "hover" get_theme_stylebox(stylebox_theme, "FlatButton").draw(ci, scroll_forwards_rect) @@ -134,8 +155,7 @@ func _draw() -> void: x_pos = prev_tab_rect.end.x else: x_pos = get_tab_rect(proposed_drop_idx).position.x - draw_line(Vector2(x_pos, 0), Vector2(x_pos, size.y), - Configs.savedata.basic_color_valid, 4) + draw_line(Vector2(x_pos, 0), Vector2(x_pos, size.y), Configs.savedata.basic_color_valid, 4) func _gui_input(event: InputEvent) -> void: @@ -196,24 +216,16 @@ func _gui_input(event: InputEvent) -> void: has_saved_tabs = true break - btn_arr.append(ContextPopup.create_shortcut_button_without_icon( - "close_tab")) + btn_arr.append(ContextPopup.create_shortcut_button_without_icon("close_tab")) # TODO Unify into "Close multiple tabs" - btn_arr.append(ContextPopup.create_shortcut_button_without_icon( - "close_all_other_tabs", tab_count < 2)) - btn_arr.append(ContextPopup.create_shortcut_button_without_icon( - "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_without_icon("close_all_other_tabs", tab_count < 2)) + btn_arr.append(ContextPopup.create_shortcut_button_without_icon("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)) + 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([6])) diff --git a/src/ui_parts/top_app_bar.gd b/src/ui_parts/top_app_bar.gd index 271bcdb..db39a66 100644 --- a/src/ui_parts/top_app_bar.gd +++ b/src/ui_parts/top_app_bar.gd @@ -52,8 +52,7 @@ func _on_more_options_pressed() -> void: "View savedata"), open_savedata_folder , false, load("res://assets/icons/OpenFolder.svg"))) - var about_btn := ContextPopup.create_shortcut_button("about_info", false, "", - load("res://assets/logos/icon.svg")) + var about_btn := ContextPopup.create_shortcut_button("about_info", false, "", load("res://assets/logos/icon.svg"), true) buttons_arr.append(about_btn) buttons_arr.append(ContextPopup.create_shortcut_button("about_repo")) var separator_indices := PackedInt32Array([1, 3]) diff --git a/src/ui_parts/viewport.gd b/src/ui_parts/viewport.gd deleted file mode 100644 index cdab875..0000000 --- a/src/ui_parts/viewport.gd +++ /dev/null @@ -1,182 +0,0 @@ -extends SubViewport - -const ZoomMenu = preload("res://src/ui_widgets/zoom_menu.gd") -const HandlesManager = preload("res://src/ui_parts/handles_manager.gd") -const DisplayTexture = preload("res://src/ui_parts/display_texture.gd") - -const BUFFER_VIEW_SPACE = 0.8 -const ZOOM_RESET_BUFFER = 0.875 - -# Holds zoom position for Ctrl + MMB zooming. -var _zoom_to: Vector2 - -var limit_left := 0.0 -var limit_right := 0.0 -var limit_top := 0.0 -var limit_bottom := 0.0 - -@onready var display: TextureRect = $Checkerboard -@onready var view: SubViewportContainer = get_parent() -@onready var controls: HandlesManager = $Controls -@onready var display_texture: DisplayTexture = $Checkerboard/DisplayTexture -@onready var reference_texture: TextureRect = $ReferenceTexture -@onready var zoom_menu: ZoomMenu = %ZoomMenu - - -func _ready() -> void: - zoom_menu.zoom_changed.connect(view.update.unbind(2)) - State.svg_resized.connect(resize) - State.viewport_size_changed.connect(adjust_view) - resize() - await get_tree().process_frame - zoom_menu.zoom_reset() - -# Top left corner. -func set_view(new_position: Vector2) -> void: - var scaled_size := size / State.zoom - view.camera_unsnapped_position = new_position.clamp(Vector2(limit_left, limit_top), - Vector2(limit_right, limit_bottom) - scaled_size) - - var stripped_left := maxf(view.camera_unsnapped_position.x, 0.0) - var stripped_top := maxf(view.camera_unsnapped_position.y, 0.0) - var stripped_right := minf(view.camera_unsnapped_position.x + scaled_size.x, - State.root_element.width) - var stripped_bottom := minf(view.camera_unsnapped_position.y + scaled_size.y, - State.root_element.height) - display_texture.view_rect = Rect2(stripped_left, stripped_top, - stripped_right - stripped_left, stripped_bottom - stripped_top) - view.update() - - -# Adjust the SVG dimensions. -func resize() -> void: - var root_element_size := State.root_element.get_size() - if root_element_size.is_finite(): - display.size = root_element_size - reference_texture.size = root_element_size - zoom_menu.zoom_reset() - -func center_frame() -> void: - var available_size := size * ZOOM_RESET_BUFFER - var w_ratio := available_size.x / State.root_element.width - var h_ratio := available_size.y / State.root_element.height - if is_finite(w_ratio) and is_finite(h_ratio): - zoom_menu.set_zoom(nearest_po2(ceili(minf(w_ratio, h_ratio) * 32)) / 64.0) - else: - zoom_menu.set_zoom(1.0) - adjust_view() - set_view((State.root_element.get_size() - size / State.zoom) / 2) - - -func _unhandled_input(event: InputEvent) -> void: - if State.get_viewport().gui_is_dragging(): - return - - if event is InputEventMouseMotion and event.button_mask & (MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_MIDDLE): - # Zooming with Ctrl + MMB. - if event.ctrl_pressed and event.button_mask == MOUSE_BUTTON_MASK_MIDDLE: - if _zoom_to == Vector2.ZERO: # Set zoom position if starting action. - _zoom_to = get_mouse_position() / Vector2(size) - zoom_menu.set_zoom(State.zoom * (1.0 + (1 if Configs.savedata.invert_zoom else -1) *\ - (wrap_mouse(event.relative).y if Configs.savedata.wraparound_panning else event.relative.y) / 128.0), _zoom_to) - # Panning with LMB or MMB. This gives a reliable way to adjust the view - # without dragging the things on it. - else: - set_view(view.camera_unsnapped_position - (wrap_mouse(event.relative) if Configs.savedata.wraparound_panning else event.relative) / State.zoom) - - elif event is InputEventPanGesture and not DisplayServer.get_name() == "Android": - # Zooming with Ctrl + touch? - if event.ctrl_pressed: - zoom_menu.set_zoom(State.zoom * (1 + event.delta.y / 2)) - # Panning with touch. - else: - set_view(view.camera_unsnapped_position + event.delta * 32 / State.zoom) - # Zooming with touch. - elif event is InputEventMagnifyGesture: - zoom_menu.set_zoom(State.zoom * event.factor) - # Actions with scrolling. - elif event is InputEventMouseButton and event.is_pressed(): - var move_vec := Vector2.ZERO - var zoom_dir := 0 - - # Zooming with scrolling. - if (not event.ctrl_pressed and not event.shift_pressed and not Configs.savedata.use_ctrl_for_zoom) or\ - (event.ctrl_pressed and Configs.savedata.use_ctrl_for_zoom): - match event.button_index: - MOUSE_BUTTON_WHEEL_UP when Configs.savedata.invert_zoom: zoom_dir = -1 - MOUSE_BUTTON_WHEEL_DOWN when Configs.savedata.invert_zoom: zoom_dir = 1 - MOUSE_BUTTON_WHEEL_UP: zoom_dir = 1 - MOUSE_BUTTON_WHEEL_DOWN: zoom_dir = -1 - _: return - # Inverted panning with Shift + scrolling. - elif event.shift_pressed: - match event.button_index: - MOUSE_BUTTON_WHEEL_UP: move_vec = Vector2.LEFT - MOUSE_BUTTON_WHEEL_DOWN: move_vec = Vector2.RIGHT - MOUSE_BUTTON_WHEEL_LEFT: move_vec = Vector2.UP - MOUSE_BUTTON_WHEEL_RIGHT: move_vec = Vector2.DOWN - _: return - # Panning with scrolling. - else: - match event.button_index: - MOUSE_BUTTON_WHEEL_UP: move_vec = Vector2.UP - MOUSE_BUTTON_WHEEL_DOWN: move_vec = Vector2.DOWN - MOUSE_BUTTON_WHEEL_LEFT: move_vec = Vector2.LEFT - MOUSE_BUTTON_WHEEL_RIGHT: move_vec = Vector2.RIGHT - _: return - - var mouse_offset := get_mouse_position() / Vector2(size) - # Apply scroll data from above. - var factor: float = event.factor - if factor == roundf(factor): # Detects if precise factor is unsuported. - factor = 1.0 - if zoom_dir == 1: - zoom_menu.zoom_in(factor, mouse_offset) - elif zoom_dir == -1: - zoom_menu.zoom_out(factor, mouse_offset) - - set_view(view.camera_unsnapped_position + move_vec * factor / State.zoom * 32) - - else: - if not event.is_echo(): - # Filter out fake mouse movement events. - if not (event is InputEventMouseMotion and event.relative == Vector2.ZERO): - _zoom_to = Vector2.ZERO # Reset Ctrl + MMB zoom position if released. - - -func _on_zoom_changed(new_zoom_level: float, offset: Vector2) -> void: - State.set_zoom(new_zoom_level) - adjust_view(offset) - display.material.set_shader_parameter("uv_scale", nearest_po2(int(State.zoom * 32.0)) / 32.0) - -var last_size_adjusted := size / State.zoom - -func adjust_view(offset := Vector2(0.5, 0.5)) -> void: - var old_size := last_size_adjusted - last_size_adjusted = size / State.zoom - - var svg_w := State.root_element.width if State.root_element.has_attribute("width") else 16384.0 - var svg_h := State.root_element.height if State.root_element.has_attribute("height") else 16384.0 - - var zoomed_size := BUFFER_VIEW_SPACE * size / State.zoom - limit_left = -zoomed_size.x - limit_right = zoomed_size.x + svg_w - limit_top = -zoomed_size.y - limit_bottom = zoomed_size.y + svg_h - - set_view(Vector2(lerpf(view.camera_unsnapped_position.x, view.camera_unsnapped_position.x + old_size.x - size.x / State.zoom, offset.x), - lerpf(view.camera_unsnapped_position.y, view.camera_unsnapped_position.y + old_size.y - size.y / State.zoom, offset.y))) - -func _on_size_changed() -> void: - State.set_viewport_size(size) - -func wrap_mouse(relative: Vector2) -> Vector2: - var view_rect := get_visible_rect().grow(-1.0) - var warp_margin := view_rect.size * 0.5 - var mouse_pos := get_mouse_position() - - if not view_rect.has_point(mouse_pos): - warp_mouse(Vector2(fposmod(mouse_pos.x, view_rect.size.x), fposmod(mouse_pos.y, view_rect.size.y))) - - return Vector2(fmod(relative.x + signf(relative.x) * warp_margin.x, view_rect.size.x), - fmod(relative.y + signf(relative.y) * warp_margin.y, view_rect.size.y)) - relative.sign() * warp_margin diff --git a/src/ui_parts/viewport.gd.uid b/src/ui_parts/viewport.gd.uid deleted file mode 100644 index c4f1783..0000000 --- a/src/ui_parts/viewport.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b6pmlbnl76wmm diff --git a/src/ui_parts/viewport_container.gd b/src/ui_parts/viewport_container.gd deleted file mode 100644 index c9d2296..0000000 --- a/src/ui_parts/viewport_container.gd +++ /dev/null @@ -1,130 +0,0 @@ -extends SubViewportContainer - -const TICKS_INTERVAL = 4 -const TICK_DISTANCE = float(TICKS_INTERVAL) - -var ci := get_canvas_item() -var grid_ci := RenderingServer.canvas_item_create() -var grid_numbers_ci := RenderingServer.canvas_item_create() - -var camera_zoom: float -var camera_position: Vector2 -var camera_unsnapped_position: Vector2 - - -func _ready() -> void: - Configs.grid_color_changed.connect(queue_redraw) - State.show_grid_changed.connect(update_show_grid) - update_show_grid() - RenderingServer.canvas_item_set_parent(grid_ci, ci) - RenderingServer.canvas_item_set_parent(grid_numbers_ci, ci) - State.svg_resized.connect(queue_redraw) - State.zoom_changed.connect(change_zoom) - State.zoom_changed.connect(queue_redraw) - -func exit_tree() -> void: - RenderingServer.free_rid(grid_numbers_ci) - -func change_zoom() -> void: - camera_zoom = State.zoom - -func update_show_grid() -> void: - RenderingServer.canvas_item_set_visible(grid_ci, State.show_grid) - RenderingServer.canvas_item_set_visible(grid_numbers_ci, State.show_grid) - - -func update() -> void: - var new_position := camera_unsnapped_position.snapped(Vector2(1, 1) / camera_zoom) - if camera_position != new_position: - camera_position = new_position - State.view_changed.emit() - - get_child(0).canvas_transform = Transform2D(0.0, Vector2(camera_zoom, camera_zoom), - 0.0, -camera_position * camera_zoom) - queue_redraw() - - -# Don't ask me to explain this. -func _draw() -> void: - RenderingServer.canvas_item_clear(grid_ci) - RenderingServer.canvas_item_clear(grid_numbers_ci) - - var axis_line_color := Color(Configs.savedata.grid_color, 0.75) - var major_grid_color := Color(Configs.savedata.grid_color, 0.35) - var minor_grid_color := Color(Configs.savedata.grid_color, 0.15) - - var grid_size := Vector2(State.viewport_size) / camera_zoom - RenderingServer.canvas_item_add_line(grid_ci, Vector2(-camera_position.x * camera_zoom, 0), - Vector2(-camera_position.x * camera_zoom, grid_size.y * camera_zoom), axis_line_color) - RenderingServer.canvas_item_add_line(grid_ci, Vector2(0, -camera_position.y * camera_zoom), - Vector2(grid_size.x * camera_zoom, -camera_position.y * camera_zoom), axis_line_color) - - var major_points := PackedVector2Array() - var minor_points := PackedVector2Array() - var draw_minor_lines := (camera_zoom >= 8.0) - var mark_pixel_lines := (camera_zoom >= 128.0) - @warning_ignore("integer_division") - var rate := nearest_po2(roundi(maxf(128.0 / (TICKS_INTERVAL * camera_zoom), 2.0))) / 2 - - var i := fmod(-camera_position.x, 1.0) - var major_line_h_offset := fposmod(-camera_position.x, TICK_DISTANCE) - # Horizontal offset. - while i <= grid_size.x: - if major_line_h_offset != fposmod(i, TICK_DISTANCE): - if draw_minor_lines: - minor_points.append(Vector2(i * camera_zoom, 0)) - minor_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) - if mark_pixel_lines: - ThemeUtils.regular_font.draw_string(grid_numbers_ci, - Vector2(i * camera_zoom + 4, 14), String.num_int64(floori(i + camera_position.x)), - HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) - else: - var coord := snappedi(i + camera_position.x, TICKS_INTERVAL) - if int(coord / TICK_DISTANCE) % rate == 0: - major_points.append(Vector2(i * camera_zoom, 0)) - major_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) - ThemeUtils.regular_font.draw_string(grid_numbers_ci, - Vector2(i * camera_zoom + 4, 14), String.num_int64(coord), - HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) - elif coord % rate == 0: - minor_points.append(Vector2(i * camera_zoom, 0)) - minor_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) - i += 1.0 - - i = fmod(-camera_position.y, 1.0) - var major_line_v_offset := fposmod(-camera_position.y, TICK_DISTANCE) - # Vertical offset. - while i < grid_size.y: - if major_line_v_offset != fposmod(i, TICK_DISTANCE): - if draw_minor_lines: - minor_points.append(Vector2(0, i * camera_zoom)) - minor_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) - if mark_pixel_lines: - ThemeUtils.regular_font.draw_string(grid_numbers_ci, - Vector2(4, i * camera_zoom + 14), String.num_int64(floori(i + camera_position.y)), - HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) - else: - var coord := snappedi(i + camera_position.y, TICKS_INTERVAL) - if int(coord / TICK_DISTANCE) % rate == 0: - major_points.append(Vector2(0, i * camera_zoom)) - major_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) - ThemeUtils.regular_font.draw_string(grid_numbers_ci, - Vector2(4, i * camera_zoom + 14), String.num_int64(coord), - HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) - elif coord % rate == 0: - minor_points.append(Vector2(0, i * camera_zoom)) - minor_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) - i += 1.0 - - if not major_points.is_empty(): - var pca := PackedColorArray() - @warning_ignore("integer_division") - pca.resize(major_points.size() / 2) - pca.fill(major_grid_color) - RenderingServer.canvas_item_add_multiline(grid_ci, major_points, pca) - if not minor_points.is_empty(): - var pca := PackedColorArray() - @warning_ignore("integer_division") - pca.resize(minor_points.size() / 2) - pca.fill(minor_grid_color) - RenderingServer.canvas_item_add_multiline(grid_ci, minor_points, pca) diff --git a/src/ui_parts/viewport_container.gd.uid b/src/ui_parts/viewport_container.gd.uid deleted file mode 100644 index c0ed287..0000000 --- a/src/ui_parts/viewport_container.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cbajqkgudfvh0 diff --git a/src/ui_widgets/Canvas.gd b/src/ui_widgets/Canvas.gd new file mode 100644 index 0000000..ed29747 --- /dev/null +++ b/src/ui_widgets/Canvas.gd @@ -0,0 +1,359 @@ +## A canvas node representing an editing area for an SVG. This node is extended for the main canvas. +## It can be used by itself for previews. +class_name Canvas extends SubViewportContainer + +const HandlesManager = preload("res://src/ui_widgets/handles_manager.gd") +const ViewportControls = preload("res://src/ui_widgets/viewport_controls.gd") + +const TICKS_INTERVAL = 4 +const TICK_DISTANCE = float(TICKS_INTERVAL) +const MIN_ZOOM = 0.125 +const MAX_ZOOM = 512.0 + +## The ratio of empty space the canvas could have when the camera is scrolled to its limits. +const BUFFER_VIEW_SPACE = 0.8 + +## The ratio of empty space the canvas could have at most after the center frame. +const ZOOM_RESET_BUFFER = 0.875 + +var root_element: ElementRoot: + set(new_value): + if root_element != new_value: + root_element = new_value + if is_instance_valid(root_element) and is_instance_valid(checkerboard): + sync_checkerboard() + +var hovered_xid := PackedInt32Array() +var selected_xids: Array[PackedInt32Array] = [] +var selection_pivot_xid := PackedInt32Array() + +var semi_hovered_xid := PackedInt32Array() +var semi_selected_xid := PackedInt32Array() +var inner_hovered := -1 +var inner_selections: Array[int] = [] +var inner_selection_pivot := -1 + +# These methods are duplicated from State. +func is_hovered(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> bool: + if propagate: + if XIDUtils.is_ancestor_or_self(hovered_xid, xid): + return true + if inner_idx == -1: + return false + return inner_hovered == inner_idx and semi_hovered_xid == xid + if inner_idx == -1: + return hovered_xid == xid + return inner_hovered == inner_idx and semi_hovered_xid == xid + +func is_selected(xid: PackedInt32Array, inner_idx := -1, propagate := false) -> bool: + if propagate: + for selected_xid in selected_xids: + if XIDUtils.is_ancestor_or_self(selected_xid, xid): + return true + if inner_idx == -1: + return false + return semi_selected_xid == xid and inner_idx in inner_selections + if inner_idx == -1: + return xid in selected_xids + return semi_selected_xid == xid and inner_idx in inner_selections + + +signal camera_zoom_changed +signal camera_center_changed + +var camera_zoom := -1.0: + set(new_value): + if camera_zoom != new_value: + camera_zoom = new_value + checkerboard.material.set_shader_parameter("uv_scale", nearest_po2(int(camera_zoom * 32.0)) / 32.0) + sync_canvas_transform() + queue_texture_update() + queue_redraw() + camera_zoom_changed.emit() + +var camera_center := Vector2.ZERO: + set(new_value): + if camera_center != new_value: + camera_center = new_value + sync_canvas_transform() + HandlerGUI.throw_mouse_motion_event() + queue_redraw() + camera_center_changed.emit() + +var view_rasterized := false +var show_grid := true +var show_handles := true + +var ci := get_canvas_item() +var grid_ci := RenderingServer.canvas_item_create() +var grid_numbers_ci := RenderingServer.canvas_item_create() + +var viewport := ViewportControls.new() +var display_texture := TextureRect.new() +var handles_manager := HandlesManager.new() +var checkerboard := TextureRect.new() + + +func _init() -> void: + stretch = true + clip_contents = true + +func _enter_tree() -> void: + viewport.size_2d_override_stretch = true + viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS + viewport.disable_3d = true + viewport.gui_snap_controls_to_pixels = false + viewport.canvas = self + add_child(viewport) + checkerboard.texture = load("res://assets/icons/Checkerboard.svg") + checkerboard.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + checkerboard.stretch_mode = TextureRect.STRETCH_TILE + checkerboard.texture_filter = TEXTURE_FILTER_NEAREST + var zoom_shader_material := ShaderMaterial.new() + zoom_shader_material.shader = load("res://src/shaders/zoom_shader.gdshader") + checkerboard.material = zoom_shader_material + viewport.add_child(checkerboard) + display_texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + display_texture.set_anchors_and_offsets_preset(PRESET_FULL_RECT) + checkerboard.add_child(display_texture) + handles_manager.mouse_filter = Control.MOUSE_FILTER_PASS + handles_manager.canvas = self + viewport.add_child(handles_manager) + +func _ready() -> void: + RenderingServer.canvas_item_set_parent(grid_ci, ci) + RenderingServer.canvas_item_set_parent(grid_numbers_ci, ci) + Configs.grid_color_changed.connect(queue_redraw) + update_show_grid() + + resized.connect(sync_canvas_transform) + resized.connect(adjust_view) + resized.connect(center_frame) + for i in 2: + await get_tree().process_frame + resized.disconnect(center_frame) + +func _exit_tree() -> void: + RenderingServer.free_rid(grid_ci) + RenderingServer.free_rid(grid_numbers_ci) + + +func toggle_view_rasterized() -> void: + view_rasterized = not view_rasterized + if camera_zoom != 1.0: + queue_texture_update() + +func toggle_show_grid() -> void: + show_grid = not show_grid + update_show_grid() + +func toggle_show_handles() -> void: + show_handles = not show_handles + handles_manager.update_show_handles() + +func center_frame() -> void: + var available_size := size * ZOOM_RESET_BUFFER + var ratio := available_size / root_element.get_size() + if ratio.is_finite(): + var new_zoom := nearest_po2(ceili(minf(ratio.x, ratio.y) * 32.0)) / 64.0 + camera_zoom = new_zoom + else: + camera_zoom = 1.0 + + adjust_view() + set_view(root_element.get_size() / 2.0) + +func sync_canvas_transform() -> void: + viewport.canvas_transform = Transform2D(0.0, Vector2(camera_zoom, camera_zoom), 0.0, -get_camera_position() * camera_zoom) + + +var texture_view_rect := Rect2(): + set(new_value): + if texture_view_rect != new_value: + texture_view_rect = new_value + queue_texture_update() + +var _texture_update_pending := false +var _texture_update_dirty_inner_markup := false + +## Use [param dirty_inner] if the inner markup has changed and needs to be restringified. +func queue_texture_update(dirty_inner := false) -> void: + _texture_update.call_deferred() + _texture_update_pending = true + _texture_update_dirty_inner_markup = _texture_update_dirty_inner_markup or dirty_inner + +var last_inner_markup: String + +func _texture_update() -> void: + if not _texture_update_pending: + return + + _texture_update_pending = false + + var image_zoom := 1.0 if view_rasterized and camera_zoom > 1.0 else camera_zoom + var pixel_size := 1 / image_zoom + + # Translate to canvas coords. + var display_rect := texture_view_rect.grow(pixel_size * 2) + display_rect.position = display_rect.position.snapped(Vector2(pixel_size, pixel_size)).maxf(0.0) + display_rect.size = display_rect.size.snapped(Vector2(pixel_size, pixel_size)) + display_rect.end = display_rect.end.min(Vector2(ceili(root_element.width), ceili(root_element.height))) + + if _texture_update_dirty_inner_markup: + _texture_update_dirty_inner_markup = false + last_inner_markup = "" + + var svg_text := SVGParser.root_cutout_to_markup(root_element, display_rect.size.x, + display_rect.size.y, Rect2(root_element.world_to_canvas(display_rect.position), + display_rect.size / root_element.canvas_transform.get_scale()), last_inner_markup) + last_inner_markup = svg_text[1] + Utils.set_control_position_fixed(display_texture, display_rect.position) + display_texture.size = display_rect.size + display_texture.texture = SVGTexture.create_from_string(svg_text[0], image_zoom) + + +func sync_checkerboard() -> void: + var root_element_size := root_element.get_size() + if root_element_size.is_finite(): + checkerboard.size = root_element_size + + +func update_show_grid() -> void: + RenderingServer.canvas_item_set_visible(grid_ci, show_grid) + RenderingServer.canvas_item_set_visible(grid_numbers_ci, show_grid) + queue_redraw() + +# Don't ask me to explain this. +func _draw() -> void: + RenderingServer.canvas_item_clear(grid_ci) + RenderingServer.canvas_item_clear(grid_numbers_ci) + + var snapped_pos := get_camera_position() + var axis_line_color := Color(Configs.savedata.grid_color, 0.75) + var major_grid_color := Color(Configs.savedata.grid_color, 0.35) + var minor_grid_color := Color(Configs.savedata.grid_color, 0.15) + + var grid_size := size / camera_zoom + RenderingServer.canvas_item_add_line(grid_ci, Vector2(-snapped_pos.x * camera_zoom, 0), + Vector2(-snapped_pos.x * camera_zoom, grid_size.y * camera_zoom), axis_line_color) + RenderingServer.canvas_item_add_line(grid_ci, Vector2(0, -snapped_pos.y * camera_zoom), + Vector2(grid_size.x * camera_zoom, -snapped_pos.y * camera_zoom), axis_line_color) + + var major_points := PackedVector2Array() + var minor_points := PackedVector2Array() + var draw_minor_lines := (camera_zoom >= 8.0) + var mark_pixel_lines := (camera_zoom >= 128.0) + @warning_ignore("integer_division") + var rate := nearest_po2(roundi(maxf(128.0 / (TICKS_INTERVAL * camera_zoom), 2.0))) / 2 + + var i := fmod(-snapped_pos.x, 1.0) + var major_line_h_offset := fposmod(-snapped_pos.x, TICK_DISTANCE) + # Horizontal offset. + while i <= grid_size.x: + if major_line_h_offset != fposmod(i, TICK_DISTANCE): + if draw_minor_lines: + minor_points.append(Vector2(i * camera_zoom, 0)) + minor_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) + if mark_pixel_lines: + ThemeUtils.regular_font.draw_string(grid_numbers_ci, + Vector2(i * camera_zoom + 4, 14), String.num_int64(floori(i + snapped_pos.x)), + HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) + else: + var coord := snappedi(i + snapped_pos.x, TICKS_INTERVAL) + if int(coord / TICK_DISTANCE) % rate == 0: + major_points.append(Vector2(i * camera_zoom, 0)) + major_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) + ThemeUtils.regular_font.draw_string(grid_numbers_ci, + Vector2(i * camera_zoom + 4, 14), String.num_int64(coord), + HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) + elif coord % rate == 0: + minor_points.append(Vector2(i * camera_zoom, 0)) + minor_points.append(Vector2(i * camera_zoom, grid_size.y * camera_zoom)) + i += 1.0 + + i = fmod(-snapped_pos.y, 1.0) + var major_line_v_offset := fposmod(-snapped_pos.y, TICK_DISTANCE) + # Vertical offset. + while i < grid_size.y: + if major_line_v_offset != fposmod(i, TICK_DISTANCE): + if draw_minor_lines: + minor_points.append(Vector2(0, i * camera_zoom)) + minor_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) + if mark_pixel_lines: + ThemeUtils.regular_font.draw_string(grid_numbers_ci, + Vector2(4, i * camera_zoom + 14), String.num_int64(floori(i + snapped_pos.y)), + HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) + else: + var coord := snappedi(i + snapped_pos.y, TICKS_INTERVAL) + if int(coord / TICK_DISTANCE) % rate == 0: + major_points.append(Vector2(0, i * camera_zoom)) + major_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) + ThemeUtils.regular_font.draw_string(grid_numbers_ci, + Vector2(4, i * camera_zoom + 14), String.num_int64(coord), + HORIZONTAL_ALIGNMENT_LEFT, -1, 14, axis_line_color) + elif coord % rate == 0: + minor_points.append(Vector2(0, i * camera_zoom)) + minor_points.append(Vector2(grid_size.x * camera_zoom, i * camera_zoom)) + i += 1.0 + + if not major_points.is_empty(): + var pca := PackedColorArray() + @warning_ignore("integer_division") + pca.resize(major_points.size() / 2) + pca.fill(major_grid_color) + RenderingServer.canvas_item_add_multiline(grid_ci, major_points, pca) + if not minor_points.is_empty(): + var pca := PackedColorArray() + @warning_ignore("integer_division") + pca.resize(minor_points.size() / 2) + pca.fill(minor_grid_color) + RenderingServer.canvas_item_add_multiline(grid_ci, minor_points, pca) + + +func get_camera_position() -> Vector2: + return (camera_center - size / camera_zoom / 2.0).snapped(Vector2(1, 1) / camera_zoom) + + +var limit_top_left := Vector2.ZERO +var limit_bottom_right := Vector2.ZERO + + +func set_zoom(new_zoom: float, offset := Vector2(0.5, 0.5)) -> void: + camera_zoom = clampf(new_zoom, MIN_ZOOM, MAX_ZOOM) + adjust_view(offset) + +# Top left corner. +func set_view(new_center: Vector2) -> void: + camera_center = new_center.clamp(limit_top_left, limit_bottom_right) + + var stripped_left := maxf(camera_center.x - size.x / camera_zoom / 2.0, 0.0) + var stripped_top := maxf(camera_center.y - size.y / camera_zoom / 2.0, 0.0) + var stripped_right := minf(camera_center.x + size.x / camera_zoom / 2.0, root_element.width) + var stripped_bottom := minf(camera_center.y + size.y / camera_zoom / 2.0, root_element.height) + texture_view_rect = Rect2(stripped_left, stripped_top, stripped_right - stripped_left, stripped_bottom - stripped_top) + + +var last_size_adjusted := Vector2.ZERO + +func adjust_view(offset := Vector2(0.5, 0.5)) -> void: + var old_size := last_size_adjusted + last_size_adjusted = size / camera_zoom + + var buffer_size := (BUFFER_VIEW_SPACE - 0.5) * size / camera_zoom + limit_top_left = -buffer_size + limit_bottom_right = buffer_size + Vector2(root_element.width if root_element.has_attribute("width") else 16384.0, + root_element.height if root_element.has_attribute("height") else 16384.0) + + set_view(camera_center + (offset - Vector2(0.5, 0.5)) * (old_size - size / camera_zoom)) + + +func wrap_mouse(relative: Vector2) -> Vector2: + var view_rect := Rect2(Vector2.ZERO, size).grow(-1.0) + var warp_margin := view_rect.size * 0.5 + var mouse_pos := get_local_mouse_position() + + if not view_rect.has_point(mouse_pos): + warp_mouse(Vector2(fposmod(mouse_pos.x, view_rect.size.x), fposmod(mouse_pos.y, view_rect.size.y))) + + return Vector2(fmod(relative.x + signf(relative.x) * warp_margin.x, view_rect.size.x), + fmod(relative.y + signf(relative.y) * warp_margin.y, view_rect.size.y)) - relative.sign() * warp_margin diff --git a/src/ui_widgets/Canvas.gd.uid b/src/ui_widgets/Canvas.gd.uid new file mode 100644 index 0000000..9c34668 --- /dev/null +++ b/src/ui_widgets/Canvas.gd.uid @@ -0,0 +1 @@ +uid://8bxv5i60h8rb diff --git a/src/ui_widgets/ContextPopup.gd b/src/ui_widgets/ContextPopup.gd index 56c0aea..7a5f0cf 100644 --- a/src/ui_widgets/ContextPopup.gd +++ b/src/ui_widgets/ContextPopup.gd @@ -8,7 +8,7 @@ func _init() -> void: theme_type_variation = "OutlinedPanel" -static func create_shortcut_button(action: String, disabled := false, custom_text := "", custom_icon: Texture2D = null) -> Button: +static func create_shortcut_button(action: String, disabled := false, custom_text := "", custom_icon: Texture2D = null, no_modulation := false) -> Button: if not InputMap.has_action(action): push_error("Non-existent shortcut was passed.") return @@ -18,7 +18,7 @@ static func create_shortcut_button(action: String, disabled := false, custom_tex if not is_instance_valid(custom_icon): custom_icon = ShortcutUtils.get_action_icon(action) var btn := create_button(custom_text, HandlerGUI.throw_action_event.bind(action), - disabled, custom_icon, ShortcutUtils.get_action_showcase_text(action)) + disabled, custom_icon, no_modulation, ShortcutUtils.get_action_showcase_text(action)) var shortcut_events := ShortcutUtils.get_action_all_valid_shortcuts(action) if not shortcut_events.is_empty(): @@ -37,7 +37,7 @@ static func create_shortcut_button_without_icon(action: String, disabled := fals if custom_text.is_empty(): custom_text = TranslationUtils.get_action_description(action, true) var btn := create_button(custom_text, HandlerGUI.throw_action_event.bind(action), - disabled, null, ShortcutUtils.get_action_showcase_text(action)) + disabled, null, false, ShortcutUtils.get_action_showcase_text(action)) var shortcut_events := ShortcutUtils.get_action_all_valid_shortcuts(action) if not shortcut_events.is_empty(): @@ -49,7 +49,7 @@ static func create_shortcut_button_without_icon(action: String, disabled := fals return btn -static func create_button(text: String, press_callback: Callable, disabled := false, icon: Texture2D = null, dim_text := "") -> Button: +static func create_button(text: String, press_callback: Callable, disabled := false, icon: Texture2D = null, no_modulation := false, dim_text := "") -> Button: # Create main button. var main_button := Button.new() main_button.theme_type_variation = "ContextButton" @@ -64,6 +64,12 @@ static func create_button(text: String, press_callback: Callable, disabled := fa if is_instance_valid(icon): main_button.add_theme_constant_override("icon_max_width", 16) main_button.icon = icon + if no_modulation: + main_button.begin_bulk_theme_override() + main_button.add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.875)) + main_button.add_theme_color_override("icon_hover_color", Color(1, 1, 1, 1)) + main_button.add_theme_color_override("icon_pressed_color", Color(1, 1, 1, 1)) + main_button.end_bulk_theme_override() if press_callback.is_valid(): main_button.pressed.connect(press_callback, CONNECT_ONE_SHOT) @@ -189,7 +195,7 @@ min_width := -1.0, max_height := -1.0, separator_indices := PackedInt32Array()) # Setup the title. var title_container := PanelContainer.new() var stylebox := StyleBoxFlat.new() - stylebox.bg_color = Color(ThemeUtils.extreme_theme_color, 0.2) + stylebox.bg_color = ThemeUtils.context_button_color_disabled stylebox.content_margin_bottom = 3.0 stylebox.content_margin_left = 8.0 stylebox.content_margin_right = 8.0 diff --git a/src/ui_widgets/basic_xnode_frame.gd b/src/ui_widgets/basic_xnode_frame.gd index 07c410f..8d7d4d5 100644 --- a/src/ui_widgets/basic_xnode_frame.gd +++ b/src/ui_widgets/basic_xnode_frame.gd @@ -55,7 +55,7 @@ func _on_title_button_pressed() -> void: func _gui_input(event: InputEvent) -> void: if event is InputEventMouseMotion and event.button_mask == 0: - if State.semi_hovered_xid != xnode.xid and not XIDUtils.is_parent(xnode.xid, State.hovered_xid): + if State.semi_hovered_xid != xnode.xid and not XIDUtils.is_ancestor(xnode.xid, State.hovered_xid): State.set_hovered(xnode.xid) elif event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT: @@ -88,44 +88,29 @@ func set_default_font_color() -> void: text_edit.add_theme_color_override("font_color", ThemeUtils.editable_text_color) func determine_selection_highlight() -> void: - var is_selected := xnode.xid in State.selected_xids - var is_hovered := State.hovered_xid == xnode.xid + var is_selected := State.is_selected(xnode.xid) + var is_hovered := State.is_hovered(xnode.xid) if is_selected: if is_hovered: - color = Color.from_hsv(0.625, 0.48, 0.27) - title_color = Color.from_hsv(0.625, 0.5, 0.38) + color = ThemeUtils.hover_selected_inspector_frame_inner_color + title_color = ThemeUtils.hover_selected_inspector_frame_title_color else: - color = Color.from_hsv(0.625, 0.5, 0.25) - title_color = Color.from_hsv(0.625, 0.6, 0.35) - border_color = Color.from_hsv(0.6, 0.75, 0.75) + color = ThemeUtils.selected_inspector_frame_inner_color + title_color = ThemeUtils.selected_inspector_frame_title_color + border_color = ThemeUtils.active_inspector_frame_border_color elif is_hovered: - color = Color.from_hsv(0.625, 0.57, 0.19) - title_color = Color.from_hsv(0.625, 0.4, 0.2) - border_color = Color.from_hsv(0.6, 0.55, 0.45) + color = ThemeUtils.hover_inspector_frame_inner_color + title_color = ThemeUtils.hover_inspector_frame_title_color + border_color = ThemeUtils.hover_inspector_frame_border_color else: - color = Color.from_hsv(0.625, 0.6, 0.16) - title_color = Color.from_hsv(0.625, 0.45, 0.17) - border_color = Color.from_hsv(0.6, 0.5, 0.35) - - if not ThemeUtils.is_theme_dark: - color.s *= 0.2 - color.v = lerpf(color.v, 1.0, 0.875) - title_color.s *= 0.2 - title_color.v = lerpf(title_color.v, 1.0, 0.875) - border_color.v = lerpf(border_color.v, 1.0, 0.8) - if is_hovered: - color.s = lerpf(color.s, 1.0, 0.05) - title_color.s = lerpf(title_color.s, 1.0, 0.05) - border_color.v *= 0.9 - if is_selected: - color.s = lerpf(color.s, 1.0, 0.15) - title_color.s = lerpf(title_color.s, 1.0, 0.15) - border_color.v *= 0.65 + color = ThemeUtils.inspector_frame_inner_color + title_color = ThemeUtils.inspector_frame_title_color + border_color = ThemeUtils.inspector_frame_border_color var depth := xnode.xid.size() - 1 - var depth_tint := depth * 0.12 if depth > 0: + var depth_tint := depth * 0.12 color.h += depth_tint border_color.h += depth_tint title_color.h += depth_tint @@ -139,7 +124,7 @@ func _draw() -> void: return for selected_xid in State.selected_xids: - if XIDUtils.is_parent_or_self(selected_xid, xnode.xid): + if XIDUtils.is_ancestor_or_self(selected_xid, xnode.xid): return var parent_xid := XIDUtils.get_parent_xid(xnode.xid) @@ -153,7 +138,7 @@ func _draw() -> void: var selected_xnode := xnode.root.get_xnode(xid) if not selected_xnode.is_element(): continue - if !DB.is_child_element_valid(drop_element.name, selected_xnode.name): + if not DB.is_child_element_valid(drop_element.name, selected_xnode.name): are_all_children_valid = false break diff --git a/src/ui_widgets/color_configuration_popup.gd b/src/ui_widgets/color_configuration_popup.gd index cac7c94..de2ed98 100644 --- a/src/ui_widgets/color_configuration_popup.gd +++ b/src/ui_widgets/color_configuration_popup.gd @@ -5,6 +5,7 @@ signal color_deletion_requested @onready var color_label: Label = %LabelContainer/ColorLabel @onready var color_name_edit: BetterLineEdit = %ConfigureContainer/TopContainer/NameEdit @onready var color_edit: LineEditButton = %ConfigureContainer/BottomContainer/ColorEdit +@onready var edit_button: Button = $ConfigureContainer/TopContainer/LabelContainer/EditButton @onready var delete_button: Button = %ConfigureContainer/BottomContainer/DeleteButton @onready var label_container: HBoxContainer = %LabelContainer @@ -18,6 +19,8 @@ func _ready() -> void: sync_localization() color_name_edit.text_submitted.connect(_on_name_edit_text_submitted) color_name_edit.text_change_canceled.connect(hide_name_edit) + edit_button.pressed.connect(_on_edit_button_pressed) + delete_button.pressed.connect(_on_delete_button_pressed) func sync_localization() -> void: diff --git a/src/ui_widgets/color_configuration_popup.tscn b/src/ui_widgets/color_configuration_popup.tscn index 81775d0..97f7981 100644 --- a/src/ui_widgets/color_configuration_popup.tscn +++ b/src/ui_widgets/color_configuration_popup.tscn @@ -43,6 +43,7 @@ icon = ExtResource("2_y6afs") [node name="NameEdit" type="LineEdit" parent="ConfigureContainer/TopContainer"] visible = false +custom_minimum_size = Vector2(80, 0) layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 4 @@ -68,6 +69,3 @@ focus_mode = 0 mouse_default_cursor_shape = 2 theme_type_variation = &"IconButton" icon = ExtResource("6_50dbg") - -[connection signal="pressed" from="ConfigureContainer/TopContainer/LabelContainer/EditButton" to="." method="_on_edit_button_pressed"] -[connection signal="pressed" from="ConfigureContainer/BottomContainer/DeleteButton" to="." method="_on_delete_button_pressed"] diff --git a/src/ui_widgets/color_edit.gd b/src/ui_widgets/color_edit.gd index 264ea52..35b2294 100644 --- a/src/ui_widgets/color_edit.gd +++ b/src/ui_widgets/color_edit.gd @@ -4,7 +4,7 @@ extends LineEditButton const ColorEditPopup = preload("res://src/ui_widgets/color_edit_popup.gd") const ColorEditPopupScene = preload("res://src/ui_widgets/color_edit_popup.tscn") -const checkerboard = preload("res://assets/icons/backgrounds/ColorButtonBG.svg") +const checkerboard = preload("res://assets/icons/CheckerboardColorButton.svg") var color_picker: ColorEditPopup diff --git a/src/ui_widgets/color_field.gd b/src/ui_widgets/color_field.gd index 59d9fee..6b764ed 100644 --- a/src/ui_widgets/color_field.gd +++ b/src/ui_widgets/color_field.gd @@ -7,16 +7,16 @@ var element: Element var attribute_name: String: # May propagate. set(new_value): attribute_name = new_value - cached_allow_url = attribute_name in DB.attribute_color_url_allowed - cached_allow_none = attribute_name in DB.attribute_color_none_allowed - cached_allow_current_color = attribute_name in DB.attribute_color_current_color_allowed + cached_allow_url = attribute_name in DB.COLOR_ATTRIBUTES_WITH_URL_ALLOWED + cached_allow_none = attribute_name in DB.COLOR_ATTRIBUTES_WITH_NONE_ALLOWED + cached_allow_current_color = attribute_name in DB.COLOR_ATTRIBUTES_WITH_CURRENT_COLOR_ALLOWED var cached_allow_url: bool var cached_allow_none: bool var cached_allow_current_color: bool const ColorFieldPopupScene = preload("res://src/ui_widgets/color_field_popup.tscn") -const checkerboard = preload("res://assets/icons/backgrounds/ColorButtonBG.svg") +const checkerboard = preload("res://assets/icons/CheckerboardColorButton.svg") var color_popup: ColorFieldPopup var gradient_texture: SVGTexture @@ -41,7 +41,7 @@ func _ready() -> void: Configs.basic_colors_changed.connect(sync) sync() element.attribute_changed.connect(_on_element_attribute_changed) - if attribute_name in DB.propagated_attributes: + if attribute_name in DB.PROPAGATED_ATTRIBUTES: element.ancestor_attribute_changed.connect(_on_element_ancestor_attribute_changed) text_submitted.connect(set_value.bind(true)) focus_entered.connect(reset_font_color) @@ -141,8 +141,7 @@ func _draw() -> void: # Draw the button border. if is_instance_valid(temp_button) and temp_button.button_pressed: draw_button_border("pressed") - elif is_instance_valid(temp_button) and temp_button.get_global_rect().has_point( - get_viewport().get_mouse_position()): + elif is_instance_valid(temp_button) and temp_button.get_global_rect().has_point(get_viewport().get_mouse_position()): draw_button_border("hover") else: draw_button_border("normal") @@ -152,12 +151,11 @@ func _on_color_picked(new_color: String, close_picker: bool) -> void: set_value(new_color, close_picker) func is_valid(color_text: String) -> bool: - return ColorParser.is_valid(ColorParser.add_hash_if_hex(color_text), false, - cached_allow_url, cached_allow_none, cached_allow_current_color) + return ColorParser.is_valid(ColorParser.add_hash_if_hex(color_text), false, cached_allow_url, cached_allow_none, cached_allow_current_color) func _on_text_changed(new_text: String) -> void: - font_color = Configs.savedata.get_validity_color(!is_valid(new_text)) + font_color = Configs.savedata.get_validity_color(not is_valid(new_text)) func sync() -> void: var new_value := element.get_attribute_value(attribute_name) diff --git a/src/ui_widgets/color_field_popup.gd b/src/ui_widgets/color_field_popup.gd index 9830454..dd004d6 100644 --- a/src/ui_widgets/color_field_popup.gd +++ b/src/ui_widgets/color_field_popup.gd @@ -44,6 +44,7 @@ var _palettes_pending_update := false # Palettes will update when visible. var swatches_list: Array[ColorSwatch] = [] # Updated manually. func _ready() -> void: + color_picker.color_changed.connect(pick_color) color_picker.is_none_keyword_available = is_none_keyword_available color_picker.is_current_color_keyword_available = (current_color_availability != CurrentColorAvailability.UNAVAILABLE) color_picker.update_keyword_button() @@ -54,8 +55,7 @@ func _ready() -> void: const CONST_ARR: PackedStringArray = ["normal", "hover", "pressed"] for theme_type in CONST_ARR: - var sb: StyleBoxFlat = switch_mode_button.get_theme_stylebox(theme_type, - "TranslucentButton").duplicate() + var sb: StyleBoxFlat = switch_mode_button.get_theme_stylebox(theme_type, "TranslucentButton").duplicate() sb.corner_radius_top_left = 0 sb.corner_radius_top_right = 0 sb.corner_radius_bottom_left = 4 diff --git a/src/ui_widgets/color_field_popup.tscn b/src/ui_widgets/color_field_popup.tscn index e0d794b..a67cad9 100644 --- a/src/ui_widgets/color_field_popup.tscn +++ b/src/ui_widgets/color_field_popup.tscn @@ -61,5 +61,3 @@ layout_mode = 2 size_flags_horizontal = 3 focus_mode = 0 mouse_default_cursor_shape = 2 - -[connection signal="color_changed" from="MainContainer/Content/ColorPicker" to="." method="pick_color"] diff --git a/src/ui_widgets/color_swatch.gd b/src/ui_widgets/color_swatch.gd index 738cda8..e9a1bc9 100644 --- a/src/ui_widgets/color_swatch.gd +++ b/src/ui_widgets/color_swatch.gd @@ -2,7 +2,7 @@ extends Button const bounds = Vector2(2, 2) -const checkerboard = preload("res://assets/icons/backgrounds/Checkerboard.svg") +const checkerboard = preload("res://assets/icons/Checkerboard.svg") var color: String var color_name: String diff --git a/src/ui_widgets/color_swatch_config.gd b/src/ui_widgets/color_swatch_config.gd index e0dd561..d95d417 100644 --- a/src/ui_widgets/color_swatch_config.gd +++ b/src/ui_widgets/color_swatch_config.gd @@ -1,6 +1,6 @@ extends Button -const checkerboard = preload("res://assets/icons/backgrounds/Checkerboard.svg") +const checkerboard = preload("res://assets/icons/Checkerboard.svg") const gear_icon = preload("res://assets/icons/GearOutlined.svg") const ColorSwatchScene = preload("res://src/ui_widgets/color_swatch.tscn") diff --git a/src/ui_widgets/dropdown.gd b/src/ui_widgets/dropdown.gd index 2467b8f..36db216 100644 --- a/src/ui_widgets/dropdown.gd +++ b/src/ui_widgets/dropdown.gd @@ -11,6 +11,7 @@ extends HBoxContainer @export var value_text_map: Dictionary = {} # Dictionary[Variant, String] @onready var line_edit: BetterLineEdit = $LineEdit +@onready var button: Button = $Button signal value_changed(new_value: Variant) var _value: Variant @@ -31,6 +32,7 @@ func _ready() -> void: line_edit.gui_input.connect(_on_line_edit_gui_input) line_edit.text_changed.connect(_on_text_changed) line_edit.text_submitted.connect(_on_text_submitted) + button.pressed.connect(_on_button_pressed) _sync_line_edit() var max_width := 0 diff --git a/src/ui_widgets/dropdown.tscn b/src/ui_widgets/dropdown.tscn index fb4ed8a..38a57d7 100644 --- a/src/ui_widgets/dropdown.tscn +++ b/src/ui_widgets/dropdown.tscn @@ -32,5 +32,3 @@ mouse_default_cursor_shape = 2 theme_type_variation = &"LeftConnectedButton" icon = ExtResource("2_4oygd") expand_icon = true - -[connection signal="pressed" from="Button" to="." method="_on_button_pressed"] diff --git a/src/ui_widgets/element_frame.gd b/src/ui_widgets/element_frame.gd index 4ad2bc1..d1413d3 100644 --- a/src/ui_widgets/element_frame.gd +++ b/src/ui_widgets/element_frame.gd @@ -107,7 +107,7 @@ func _on_title_button_pressed() -> void: func _gui_input(event: InputEvent) -> void: if event is InputEventMouseMotion and event.button_mask == 0: - if State.semi_hovered_xid != element.xid and not XIDUtils.is_parent(element.xid, State.hovered_xid): + if State.semi_hovered_xid != element.xid and not XIDUtils.is_ancestor(element.xid, State.hovered_xid): State.set_hovered(element.xid) elif event is InputEventMouseButton: if event.button_index == MOUSE_BUTTON_LEFT: @@ -194,44 +194,29 @@ func get_inner_rect(idx: int) -> Rect2: return Rect2() func determine_selection_highlight() -> void: - var is_selected := element.xid in State.selected_xids - var is_hovered := State.hovered_xid == element.xid + var is_selected := State.is_selected(element.xid) + var is_hovered := State.is_hovered(element.xid) if is_selected: if is_hovered: - color = Color.from_hsv(0.625, 0.48, 0.27) - title_color = Color.from_hsv(0.625, 0.5, 0.38) + color = ThemeUtils.hover_selected_inspector_frame_inner_color + title_color = ThemeUtils.hover_selected_inspector_frame_title_color else: - color = Color.from_hsv(0.625, 0.5, 0.25) - title_color = Color.from_hsv(0.625, 0.6, 0.35) - border_color = Color.from_hsv(0.6, 0.75, 0.75) + color = ThemeUtils.selected_inspector_frame_inner_color + title_color = ThemeUtils.selected_inspector_frame_title_color + border_color = ThemeUtils.active_inspector_frame_border_color elif is_hovered: - color = Color.from_hsv(0.625, 0.57, 0.19) - title_color = Color.from_hsv(0.625, 0.4, 0.2) - border_color = Color.from_hsv(0.6, 0.55, 0.45) + color = ThemeUtils.hover_inspector_frame_inner_color + title_color = ThemeUtils.hover_inspector_frame_title_color + border_color = ThemeUtils.hover_inspector_frame_border_color else: - color = Color.from_hsv(0.625, 0.6, 0.16) - title_color = Color.from_hsv(0.625, 0.45, 0.17) - border_color = Color.from_hsv(0.6, 0.5, 0.35) - - if not ThemeUtils.is_theme_dark: - color.s *= 0.2 - color.v = lerpf(color.v, 1.0, 0.875) - title_color.s *= 0.2 - title_color.v = lerpf(title_color.v, 1.0, 0.875) - border_color.v = lerpf(border_color.v, 1.0, 0.8) - if is_hovered: - color.s = lerpf(color.s, 1.0, 0.05) - title_color.s = lerpf(title_color.s, 1.0, 0.05) - border_color.v *= 0.9 - if is_selected: - color.s = lerpf(color.s, 1.0, 0.15) - title_color.s = lerpf(title_color.s, 1.0, 0.15) - border_color.v *= 0.65 + color = ThemeUtils.inspector_frame_inner_color + title_color = ThemeUtils.inspector_frame_title_color + border_color = ThemeUtils.inspector_frame_border_color var depth := element.xid.size() - 1 - var depth_tint := depth * 0.12 if depth > 0: + var depth_tint := depth * 0.12 color.h += depth_tint border_color.h += depth_tint title_color.h += depth_tint @@ -245,7 +230,7 @@ func _draw() -> void: return for selected_xid in State.selected_xids: - if XIDUtils.is_parent_or_self(selected_xid, element.xid): + if XIDUtils.is_ancestor_or_self(selected_xid, element.xid): return var parent_xid := XIDUtils.get_parent_xid(element.xid) @@ -258,7 +243,7 @@ func _draw() -> void: var are_all_children_valid := true for xid in State.selected_xids: var xnode := root_element.get_xnode(xid) - if xnode.is_element() and !DB.is_child_element_valid(drop_tag.name, xnode.name): + if xnode.is_element() and not DB.is_child_element_valid(drop_tag.name, xnode.name): are_all_children_valid = false break diff --git a/src/ui_widgets/enum_field.gd b/src/ui_widgets/enum_field.gd index 2e0aada..e6d384c 100644 --- a/src/ui_widgets/enum_field.gd +++ b/src/ui_widgets/enum_field.gd @@ -20,7 +20,7 @@ func _ready() -> void: Configs.basic_colors_changed.connect(sync) sync() element.attribute_changed.connect(_on_element_attribute_changed) - if attribute_name in DB.propagated_attributes: + if attribute_name in DB.PROPAGATED_ATTRIBUTES: element.ancestor_attribute_changed.connect(_on_element_ancestor_attribute_changed) text_submitted.connect(_on_text_submitted) focus_entered.connect(reset_font_color) @@ -50,7 +50,7 @@ func _on_pressed() -> void: reset_btn.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER btn_arr.append(reset_btn) # Add a button for each enum value. - for enum_constant in DB.attribute_enum_values[attribute_name]: + for enum_constant in DB.ATTRIBUTE_ENUM_VALUES[attribute_name]: var btn := ContextPopup.create_button(enum_constant, set_value.bind(enum_constant, true), enum_constant == element.get_attribute_value(attribute_name)) @@ -63,7 +63,7 @@ func _on_pressed() -> void: func _on_text_submitted(new_text: String) -> void: - if new_text.is_empty() or new_text in DB.attribute_enum_values[attribute_name]: + if new_text.is_empty() or new_text in DB.ATTRIBUTE_ENUM_VALUES[attribute_name]: set_value(new_text, true) else: sync() @@ -71,7 +71,7 @@ func _on_text_submitted(new_text: String) -> void: func _on_text_changed(new_text: String) -> void: font_color = Configs.savedata.get_validity_color( - not new_text in DB.attribute_enum_values[attribute_name]) + not new_text in DB.ATTRIBUTE_ENUM_VALUES[attribute_name]) func sync() -> void: diff --git a/src/ui_widgets/flag_field.gd b/src/ui_widgets/flag_field.gd index 106c059..7016a5f 100644 --- a/src/ui_widgets/flag_field.gd +++ b/src/ui_widgets/flag_field.gd @@ -21,6 +21,9 @@ func _on_toggled(is_state_pressed: bool) -> void: func _ready() -> void: value_changed.connect(_on_value_changed) + toggled.connect(_on_toggled) + mouse_entered.connect(_on_mouse_entered) + mouse_exited.connect(_on_mouse_exited) button_pressed = (get_value() == 1) text = String.num_uint64(get_value()) diff --git a/src/ui_widgets/flag_field.tscn b/src/ui_widgets/flag_field.tscn index 1073ca1..c060b32 100644 --- a/src/ui_widgets/flag_field.tscn +++ b/src/ui_widgets/flag_field.tscn @@ -52,7 +52,3 @@ theme_override_styles/disabled = SubResource("StyleBoxEmpty_kfgda") toggle_mode = true text = "0" script = ExtResource("2_0bhg4") - -[connection signal="mouse_entered" from="." to="." method="_on_mouse_entered"] -[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"] -[connection signal="toggled" from="." to="." method="_on_toggled"] diff --git a/src/ui_widgets/good_color_picker.gd b/src/ui_widgets/good_color_picker.gd index 257d612..1274515 100644 --- a/src/ui_widgets/good_color_picker.gd +++ b/src/ui_widgets/good_color_picker.gd @@ -5,7 +5,7 @@ const EyedropperPopupScene = preload("res://src/ui_parts/eyedropper_popup.tscn") const handle_texture = preload("res://assets/icons/BWHandle.svg") const slider_arrow = preload("res://assets/icons/SliderArrow.svg") const side_slider_arrow = preload("res://assets/icons/SideSliderArrow.svg") -const bg_pattern = preload("res://assets/icons/backgrounds/CheckerboardMini.svg") +const bg_pattern = preload("res://assets/icons/CheckerboardMini.svg") var alpha_enabled := false var is_none_keyword_available := false @@ -150,15 +150,33 @@ func _ready() -> void: Configs.theme_changed.connect(sync_color_space_buttons) sync_color_space_buttons() # Set up signals. + color_wheel.gui_input.connect(_on_color_wheel_gui_input) + start_color_rect.draw.connect(_on_start_color_rect_draw) + color_rect.draw.connect(_on_color_rect_draw) + reset_color_button.pressed.connect(_on_reset_color_button_pressed) + + widgets_arr[0].draw.connect(_on_side_slider_draw) + widgets_arr[1].draw.connect(_on_slider1_draw) + widgets_arr[2].draw.connect(_on_slider2_draw) + widgets_arr[3].draw.connect(_on_slider3_draw) widgets_arr[0].gui_input.connect(parse_slider_input.bind(0, true)) widgets_arr[1].gui_input.connect(parse_slider_input.bind(1)) widgets_arr[2].gui_input.connect(parse_slider_input.bind(2)) widgets_arr[3].gui_input.connect(parse_slider_input.bind(3)) + tracks_arr[1].resized.connect(_on_track_resized) + tracks_arr[2].resized.connect(_on_track_resized) + tracks_arr[3].resized.connect(_on_track_resized) + fields_arr[1].text_submitted.connect(_on_slider1_text_submitted) + fields_arr[2].text_submitted.connect(_on_slider2_text_submitted) + fields_arr[3].text_submitted.connect(_on_slider3_text_submitted) slider_mode_changed.connect(_on_slider_mode_changed) _on_slider_mode_changed() if alpha_enabled: alpha_slider.visible = alpha_enabled + widgets_arr[4].draw.connect(_on_slider4_draw) widgets_arr[4].gui_input.connect(parse_slider_input.bind(4)) + tracks_arr[4].resized.connect(_on_track_resized) + fields_arr[4].text_submitted.connect(_on_slider4_text_submitted) update_keyword_button() eyedropper_button.pressed.connect(_on_eyedropper_pressed) @@ -372,7 +390,7 @@ func slider3_update() -> void: _slider_set_text(fields_arr[3], number) func slider4_update() -> void: - _slider_set_text(fields_arr[4],display_color.a * 100) + _slider_set_text(fields_arr[4], display_color.a * 100) func _slider_set_text(field: BetterLineEdit, number: float) -> void: field.text = String.num_uint64(roundi(number)) @@ -381,13 +399,11 @@ func _slider_set_text(field: BetterLineEdit, number: float) -> void: func _on_keyword_button_pressed() -> void: var btn_arr: Array[Button] = [] if is_none_keyword_available: - btn_arr.append(ContextPopup.create_button("none", - set_to_keyword.bind("none"), color == "none", - load("res://assets/icons/NoneColor.svg"))) + btn_arr.append(ContextPopup.create_button("none", set_to_keyword.bind("none"), + color == "none", load("res://assets/icons/NoneColor.svg"))) if is_current_color_keyword_available: - btn_arr.append(ContextPopup.create_button("currentColor", - set_to_keyword.bind("currentColor"), color == "currentColor", - load("res://assets/icons/Paste.svg"))) + btn_arr.append(ContextPopup.create_button("currentColor", set_to_keyword.bind("currentColor"), + color == "currentColor", load("res://assets/icons/Paste.svg"))) for btn in btn_arr: btn.add_theme_font_override("font", ThemeUtils.mono_font) @@ -428,8 +444,7 @@ func _on_start_color_rect_draw() -> void: var cross_color := Color(0.8, 0.8, 0.8) start_color_rect.draw_rect(rect, Color(0.6, 0.6, 0.6)) start_color_rect.draw_line(Vector2.ZERO, rect_size, cross_color, 0.5, true) - start_color_rect.draw_line(Vector2(rect_size.x, 0), Vector2(0, rect_size.y), - cross_color, 0.5, true) + start_color_rect.draw_line(Vector2(rect_size.x, 0), Vector2(0, rect_size.y), cross_color, 0.5, true) else: start_color_rect.draw_texture_rect(bg_pattern, rect, true) start_color_rect.draw_rect(rect, starting_display_color) @@ -465,7 +480,7 @@ func draw_hslider(idx: int, offset: float, chr: String) -> void: # Make sure the arrows are redrawn when the tracks finish resizing. func _on_track_resized() -> void: - if !widgets_arr.is_empty(): + if not widgets_arr.is_empty(): queue_redraw_widgets() func queue_redraw_widgets() -> void: diff --git a/src/ui_widgets/good_color_picker.tscn b/src/ui_widgets/good_color_picker.tscn index 63173fc..daf7670 100644 --- a/src/ui_widgets/good_color_picker.tscn +++ b/src/ui_widgets/good_color_picker.tscn @@ -6,7 +6,7 @@ [ext_resource type="Shader" uid="uid://b5e666h18rkbo" path="res://src/shaders/slider_visuals.gdshader" id="5_acxpg"] [ext_resource type="Texture2D" uid="uid://cvh3kwbucf2n1" path="res://assets/icons/Reload.svg" id="5_rh0xc"] [ext_resource type="Script" uid="uid://1hox6gd5pxku" path="res://src/ui_widgets/BetterLineEdit.gd" id="6_aqyoh"] -[ext_resource type="Texture2D" uid="uid://stpallv5q0rb" path="res://assets/icons/backgrounds/CheckerboardMini.svg" id="6_qq1j3"] +[ext_resource type="Texture2D" uid="uid://stpallv5q0rb" path="res://assets/icons/CheckerboardMini.svg" id="6_qq1j3"] [ext_resource type="Texture2D" uid="uid://brff7fx0puj6" path="res://assets/icons/Eyedropper.svg" id="6_titg4"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_wl372"] @@ -276,21 +276,3 @@ theme_type_variation = &"GoodColorPickerLineEdit" alignment = 1 max_length = 3 script = ExtResource("6_aqyoh") - -[connection signal="gui_input" from="ShapeContainer/ColorWheel" to="." method="_on_color_wheel_gui_input"] -[connection signal="draw" from="ShapeContainer/SideSlider" to="." method="_on_side_slider_draw"] -[connection signal="draw" from="ColorContainer/ColorsDisplay/StartColorRect" to="." method="_on_start_color_rect_draw"] -[connection signal="draw" from="ColorContainer/ColorsDisplay/ColorRect" to="." method="_on_color_rect_draw"] -[connection signal="pressed" from="ColorContainer/ColorsDisplay/ColorRect/ResetColorButton" to="." method="_on_reset_color_button_pressed"] -[connection signal="draw" from="SliderContainer/HBoxContainer/TracksContainer/Slider1/MarginContainer" to="." method="_on_slider1_draw"] -[connection signal="resized" from="SliderContainer/HBoxContainer/TracksContainer/Slider1/MarginContainer/ColorTrack" to="." method="_on_track_resized"] -[connection signal="text_submitted" from="SliderContainer/HBoxContainer/TracksContainer/Slider1/IntField" to="." method="_on_slider1_text_submitted"] -[connection signal="draw" from="SliderContainer/HBoxContainer/TracksContainer/Slider2/MarginContainer" to="." method="_on_slider2_draw"] -[connection signal="resized" from="SliderContainer/HBoxContainer/TracksContainer/Slider2/MarginContainer/ColorTrack" to="." method="_on_track_resized"] -[connection signal="text_submitted" from="SliderContainer/HBoxContainer/TracksContainer/Slider2/IntField" to="." method="_on_slider2_text_submitted"] -[connection signal="draw" from="SliderContainer/HBoxContainer/TracksContainer/Slider3/MarginContainer" to="." method="_on_slider3_draw"] -[connection signal="resized" from="SliderContainer/HBoxContainer/TracksContainer/Slider3/MarginContainer/ColorTrack" to="." method="_on_track_resized"] -[connection signal="text_submitted" from="SliderContainer/HBoxContainer/TracksContainer/Slider3/IntField" to="." method="_on_slider3_text_submitted"] -[connection signal="draw" from="SliderContainer/HBoxContainer/TracksContainer/Slider4/MarginContainer" to="." method="_on_slider4_draw"] -[connection signal="resized" from="SliderContainer/HBoxContainer/TracksContainer/Slider4/MarginContainer/ColorTrack" to="." method="_on_track_resized"] -[connection signal="text_submitted" from="SliderContainer/HBoxContainer/TracksContainer/Slider4/IntField" to="." method="_on_slider4_text_submitted"] diff --git a/src/ui_parts/handles_manager.gd b/src/ui_widgets/handles_manager.gd similarity index 90% rename from src/ui_parts/handles_manager.gd rename to src/ui_widgets/handles_manager.gd index efb9398..29de024 100644 --- a/src/ui_parts/handles_manager.gd +++ b/src/ui_widgets/handles_manager.gd @@ -25,7 +25,9 @@ var selected_color: Color var hovered_selected_color: Color # FIXME this shouldn't be needed, but otherwise the shader doesn't want to work. -@onready var animated_stroke_hacky_fix_node := Control.new() +var animated_stroke_hacky_fix_node := Control.new() + +var canvas: Canvas func _exit_tree() -> void: @@ -105,17 +107,14 @@ func _ready() -> void: State.svg_unknown_change.connect(queue_update_handles) State.selection_changed.connect(queue_redraw) State.hover_changed.connect(queue_redraw) - State.zoom_changed.connect(queue_redraw) + canvas.camera_zoom_changed.connect(queue_redraw) State.handle_added.connect(_on_handle_added) - State.view_changed.connect(_on_view_changed) queue_update_handles() - - State.show_handles_changed.connect(update_show_handles) update_show_handles() func update_show_handles() -> void: - visible = State.show_handles + visible = canvas.show_handles HandlerGUI.throw_mouse_motion_event() func queue_update_handles() -> void: @@ -128,7 +127,7 @@ func update_handles() -> void: _handles_update_pending = false handles.clear() - for element in State.root_element.get_all_valid_element_descendants(): + for element in canvas.root_element.get_all_valid_element_descendants(): match element.name: "circle": handles.append(XYHandle.new(element, "cx", "cy")) @@ -155,7 +154,7 @@ func update_handles() -> void: queue_redraw() func sync_handles(xid: PackedInt32Array) -> void: - var element := State.root_element.get_xnode(xid) + var element := canvas.root_element.get_xnode(xid) if not (element is ElementPath or element is ElementPolygon or element is ElementPolyline): queue_redraw() return @@ -212,10 +211,10 @@ func _draw() -> void: var selection_transforms: Array[Transform2D] = [] var selection_rects: Array[Rect2] = [] - for element: Element in State.root_element.get_all_valid_element_descendants(): + for element: Element in canvas.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) + var element_hovered := canvas.is_hovered(element.xid, -1, true) + var element_selected := canvas.is_selected(element.xid, -1, true) match element.name: "circle": @@ -250,10 +249,10 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) @@ -290,10 +289,10 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) @@ -359,10 +358,10 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) @@ -390,10 +389,10 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) @@ -405,10 +404,10 @@ func _draw() -> void: var current_mode := Utils.InteractionType.NONE for idx in range(1, point_list.size()): current_mode = Utils.InteractionType.NONE - if State.is_hovered(element.xid, idx, true): + if canvas.is_hovered(element.xid, idx, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.HOVERED - if State.is_selected(element.xid, idx, true): + if canvas.is_selected(element.xid, idx, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.SELECTED @@ -426,10 +425,10 @@ func _draw() -> void: if element.name == "polygon" and point_list.size() > 2: current_mode = Utils.InteractionType.NONE - if State.is_hovered(element.xid, 0, true): + if canvas.is_hovered(element.xid, 0, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.HOVERED - if State.is_selected(element.xid, 0, true): + if canvas.is_selected(element.xid, 0, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.SELECTED @@ -449,10 +448,10 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) @@ -473,10 +472,10 @@ func _draw() -> void: var relative := cmd.relative current_mode = Utils.InteractionType.NONE - if State.is_hovered(element.xid, cmd_idx, true): + if canvas.is_hovered(element.xid, cmd_idx, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.HOVERED - if State.is_selected(element.xid, cmd_idx, true): + if canvas.is_selected(element.xid, cmd_idx, true): @warning_ignore("int_as_enum_without_cast") current_mode += Utils.InteractionType.SELECTED @@ -660,17 +659,17 @@ func _draw() -> void: var bounding_box: Rect2 = element.get_bounding_box() if bounding_box.has_area(): var element_transform := element.get_transform() - var canvas_transform := State.root_element.canvas_transform + var canvas_transform := canvas.root_element.canvas_transform var canvas_scale := canvas_transform.get_scale().x var element_scale := element_transform.get_scale() - var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / State.zoom / canvas_scale + var grow_amount_unscaled := (2.0 + Configs.savedata.selection_rectangle_width) / canvas.camera_zoom / canvas_scale var grow_amount_x := grow_amount_unscaled / element_scale.x var grow_amount_y := grow_amount_unscaled / element_scale.y selection_transforms.append(canvas_transform * element_transform) selection_rects.append(bounding_box.grow_individual(grow_amount_x, grow_amount_y, grow_amount_x, grow_amount_y)) - draw_set_transform_matrix(State.root_element.canvas_transform) - RenderingServer.canvas_item_set_transform(surface, Transform2D(0.0, Vector2(1, 1) / State.zoom, 0.0, Vector2.ZERO)) + draw_set_transform_matrix(canvas.root_element.canvas_transform) + RenderingServer.canvas_item_set_transform(surface, Transform2D(0.0, Vector2(1, 1) / canvas.camera_zoom, 0.0, Vector2.ZERO)) # First gather all handles in 4 categories, to then draw them in the right order. var normal_handles: Array[Handle] = [] @@ -683,8 +682,8 @@ func _draw() -> void: inner_idx = handle.command_index elif handle is PolyHandle: inner_idx = handle.point_index - var is_hovered := State.is_hovered(handle.element.xid, inner_idx, true) - var is_selected := State.is_selected(handle.element.xid, inner_idx, true) + var is_hovered := canvas.is_hovered(handle.element.xid, inner_idx, true) + var is_selected := canvas.is_selected(handle.element.xid, inner_idx, true) if is_hovered and is_selected: hovered_selected_handles.append(handle) @@ -716,18 +715,18 @@ handle_texture_dictionary: Dictionary[Handle.Display, Texture2D]) -> void: color_array.resize(polyline.size()) color_array.fill(color) for idx in polyline.size(): - polyline[idx] = State.root_element.canvas_to_world(polyline[idx]) * State.zoom + polyline[idx] = canvas.root_element.canvas_to_world(polyline[idx]) * canvas.camera_zoom RenderingServer.canvas_item_add_polyline(surface, polyline, color_array, CONTOUR_WIDTH, true) if not multiline.is_empty(): for idx in multiline.size(): - multiline[idx] = State.root_element.canvas_to_world(multiline[idx]) * State.zoom + multiline[idx] = canvas.root_element.canvas_to_world(multiline[idx]) * canvas.camera_zoom var color_array := PackedColorArray() color_array.resize(int(multiline.size() / 2.0)) color_array.fill(Color(color, TANGENT_ALPHA)) RenderingServer.canvas_item_add_multiline(surface, multiline, color_array, TANGENT_WIDTH, true) for handle in handles_array: var texture := handle_texture_dictionary[handle.display_mode] - texture.draw(surface, State.root_element.canvas_to_world(handle.transform * handle.pos) * State.zoom - texture.get_size() / 2) + texture.draw(surface, canvas.root_element.canvas_to_world(handle.transform * handle.pos) * canvas.camera_zoom - texture.get_size() / 2) var dragged_handle: Handle = null @@ -745,9 +744,9 @@ func _unhandled_input(event: InputEvent) -> void: State.clear_all_hovered() # Set the nearest handle as hovered, if any handles are within range. - if visible and ((event is InputEventMouseMotion and !is_instance_valid(dragged_handle) and event.button_mask == 0) or\ + if visible and ((event is InputEventMouseMotion and not is_instance_valid(dragged_handle) and event.button_mask == 0) or\ (event is InputEventMouseButton and (event.button_index in [MOUSE_BUTTON_LEFT, MOUSE_BUTTON_RIGHT]))): - var nearest_handle := find_nearest_handle(event.position / State.zoom + get_parent().get_parent().camera_position) + var nearest_handle := find_nearest_handle(event.position / canvas.camera_zoom + canvas.get_camera_position()) if is_instance_valid(nearest_handle): hovered_handle = nearest_handle if hovered_handle is PathHandle: @@ -771,7 +770,7 @@ func _unhandled_input(event: InputEvent) -> void: var event_pos := get_event_pos(event) var new_pos := Utils64Bit.transform_vector_mult( Utils64Bit.get_transform_affine_inverse(dragged_handle.precise_transform), - State.root_element.world_to_canvas_64_bit(event_pos)) + canvas.root_element.world_to_canvas_64_bit(event_pos)) dragged_handle.set_pos(new_pos) was_handle_moved = true accept_event() @@ -808,23 +807,23 @@ func _unhandled_input(event: InputEvent) -> void: if was_handle_moved: var new_pos := Utils64Bit.transform_vector_mult( Utils64Bit.get_transform_affine_inverse(dragged_handle.precise_transform), - State.root_element.world_to_canvas_64_bit(event_pos)) + canvas.root_element.world_to_canvas_64_bit(event_pos)) dragged_handle.set_pos(new_pos) State.queue_svg_save() was_handle_moved = false dragged_handle = null - elif !is_instance_valid(hovered_handle) and event.is_pressed(): + elif not is_instance_valid(hovered_handle) and event.is_pressed(): should_deselect_all = true - elif !is_instance_valid(hovered_handle) and event.is_released() and should_deselect_all: + elif not is_instance_valid(hovered_handle) and event.is_released() and should_deselect_all: dragged_handle = null State.clear_all_selections() elif event.button_index == MOUSE_BUTTON_RIGHT and event.is_pressed(): var vp := get_viewport() var popup_pos := vp.get_mouse_position() - if !is_instance_valid(hovered_handle): + if not is_instance_valid(hovered_handle): State.clear_all_selections() HandlerGUI.popup_under_pos(create_element_context( - State.root_element.world_to_canvas_64_bit(event_pos)), popup_pos, vp) + canvas.root_element.world_to_canvas_64_bit(event_pos)), popup_pos, vp) elif visible: var hovered_xid := hovered_handle.element.xid var inner_idx := -1 @@ -841,9 +840,9 @@ func _unhandled_input(event: InputEvent) -> void: func find_nearest_handle(event_pos: Vector2) -> Handle: var nearest_handle: Handle = null - var nearest_dist_squared := DEFAULT_GRAB_DISTANCE_SQUARED * (Configs.savedata.handle_size * Configs.savedata.handle_size) / (State.zoom * State.zoom) + var nearest_dist_squared := DEFAULT_GRAB_DISTANCE_SQUARED * (Configs.savedata.handle_size / canvas.camera_zoom) ** 2 for handle in handles: - var dist_to_handle_squared := event_pos.distance_squared_to(State.root_element.canvas_to_world(handle.transform * handle.pos)) + var dist_to_handle_squared := event_pos.distance_squared_to(canvas.root_element.canvas_to_world(handle.transform * handle.pos)) if dist_to_handle_squared < nearest_dist_squared: nearest_dist_squared = dist_to_handle_squared nearest_handle = handle @@ -851,10 +850,10 @@ func find_nearest_handle(event_pos: Vector2) -> Handle: # Two 64-bit coordinates instead of a Vector2. func get_event_pos(event: InputEvent) -> PackedFloat64Array: - return apply_snap(event.position / State.zoom + get_parent().get_parent().camera_position) + return apply_snap(event.position / canvas.camera_zoom + canvas.get_camera_position()) func apply_snap(pos: Vector2) -> PackedFloat64Array: - var precision_snap := 0.1 ** maxi(ceili(-log(1.0 / State.zoom) / log(10)), 0) + var precision_snap := 0.1 ** maxi(ceili(-log(1.0 / canvas.camera_zoom) / log(10)), 0) var configured_snap := absf(Configs.savedata.snap) var snap_size: float # To be used for the snap. @@ -868,19 +867,16 @@ func apply_snap(pos: Vector2) -> PackedFloat64Array: return PackedFloat64Array([snappedf(pos.x, snap_size), snappedf(pos.y, snap_size)]) -func _on_view_changed() -> void: - HandlerGUI.throw_mouse_motion_event() - func _on_handle_added() -> void: if not get_viewport_rect().has_point(get_viewport().get_mouse_position()): if not State.semi_selected_xid.is_empty(): - State.root_element.get_xnode(State.semi_selected_xid).get_attribute("d").sync_after_commands_change() + canvas.root_element.get_xnode(State.semi_selected_xid).get_attribute("d").sync_after_commands_change() State.queue_svg_save() return update_handles() var first_inner_selection := State.inner_selections[0] - if State.root_element.get_xnode(State.semi_selected_xid).get_attribute("d").get_commands()[first_inner_selection].command_char in "Zz": + if canvas.root_element.get_xnode(State.semi_selected_xid).get_attribute("d").get_commands()[first_inner_selection].command_char in "Zz": dragged_handle = null State.queue_svg_save() return @@ -892,7 +888,7 @@ func _on_handle_added() -> void: # Move the handle that's being dragged. var mouse_pos := apply_snap(get_global_mouse_position()) var new_pos := Utils64Bit.transform_vector_mult(Utils64Bit.get_transform_affine_inverse(dragged_handle.precise_transform), - State.root_element.world_to_canvas_64_bit(mouse_pos)) + canvas.root_element.world_to_canvas_64_bit(mouse_pos)) dragged_handle.set_pos(new_pos) was_handle_moved = true return @@ -910,5 +906,5 @@ func create_element_context(precise_pos: PackedFloat64Array) -> ContextPopup: return element_context func add_shape_at_pos(element_name: String, precise_pos: PackedFloat64Array) -> void: - State.root_element.add_xnode(DB.element_with_setup(element_name, [precise_pos]), PackedInt32Array([State.root_element.get_child_count()])) + canvas.root_element.add_xnode(DB.element_with_setup(element_name, [precise_pos]), PackedInt32Array([canvas.root_element.get_child_count()])) State.queue_svg_save() diff --git a/src/ui_widgets/handles_manager.gd.uid b/src/ui_widgets/handles_manager.gd.uid new file mode 100644 index 0000000..ecb52f9 --- /dev/null +++ b/src/ui_widgets/handles_manager.gd.uid @@ -0,0 +1 @@ +uid://bwu4ir0lskugq diff --git a/src/ui_widgets/mini_number_field.gd b/src/ui_widgets/mini_number_field.gd index 0d39646..e205021 100644 --- a/src/ui_widgets/mini_number_field.gd +++ b/src/ui_widgets/mini_number_field.gd @@ -14,9 +14,9 @@ func _ready() -> void: func set_value(new_value: float, no_signal := false) -> void: if not is_finite(new_value): - text = NumstringParser.basic_num_to_text(_value) + text = NumstringParser.basic_num_to_text(_value, mode == Mode.ANGLE or mode == Mode.HALF_ANGLE) return - text = NumstringParser.basic_num_to_text(new_value) + text = NumstringParser.basic_num_to_text(new_value, mode == Mode.ANGLE or mode == Mode.HALF_ANGLE) if new_value != _value: _value = new_value if not no_signal: diff --git a/src/ui_widgets/number_field.gd b/src/ui_widgets/number_field.gd index 2d48651..83fef0b 100644 --- a/src/ui_widgets/number_field.gd +++ b/src/ui_widgets/number_field.gd @@ -5,7 +5,7 @@ var element: Element var attribute_name: String: # May propagate. set(new_value): attribute_name = new_value - if DB.attribute_number_range[attribute_name] == DB.NumberRange.ARBITRARY: + if DB.ATTRIBUTE_NUMBER_RANGE[attribute_name] == DB.NumberRange.ARBITRARY: cached_min_value = -INF cached_max_value = INF else: @@ -20,7 +20,7 @@ func set_value(new_value: String, save := false) -> void: if not AttributeNumeric.text_check_percentage(new_value): var numeric_value := NumstringParser.evaluate(new_value) # Validate the value. - if !is_finite(numeric_value): + if not is_finite(numeric_value): sync() return @@ -39,7 +39,7 @@ func _ready() -> void: Configs.basic_colors_changed.connect(sync) sync() element.attribute_changed.connect(_on_element_attribute_changed) - if attribute_name in DB.propagated_attributes: + if attribute_name in DB.PROPAGATED_ATTRIBUTES: element.ancestor_attribute_changed.connect(_on_element_ancestor_attribute_changed) # These default attributes can change when cx and cy change. if element is ElementRadialGradient and attribute_name in ["fx", "fy"]: diff --git a/src/ui_widgets/number_field_with_slider.gd b/src/ui_widgets/number_field_with_slider.gd index 444f0fa..59033da 100644 --- a/src/ui_widgets/number_field_with_slider.gd +++ b/src/ui_widgets/number_field_with_slider.gd @@ -14,7 +14,7 @@ func set_value(new_value: String, save := false) -> void: if not AttributeNumeric.text_check_percentage(new_value): var numeric_value := NumstringParser.evaluate(new_value) # Validate the value. - if !is_finite(numeric_value): + if not is_finite(numeric_value): sync() return @@ -40,7 +40,7 @@ func _ready() -> void: Configs.basic_colors_changed.connect(sync) sync() element.attribute_changed.connect(_on_element_attribute_changed) - if attribute_name in DB.propagated_attributes: + if attribute_name in DB.PROPAGATED_ATTRIBUTES: element.ancestor_attribute_changed.connect(_on_element_ancestor_attribute_changed) text_submitted.connect(set_value.bind(true)) focus_entered.connect(reset_font_color) diff --git a/src/ui_widgets/palette_config.gd b/src/ui_widgets/palette_config.gd index 46c402e..94832f7 100644 --- a/src/ui_widgets/palette_config.gd +++ b/src/ui_widgets/palette_config.gd @@ -77,6 +77,7 @@ func rebuild_colors() -> void: fake_swatch.focus_mode = Control.FOCUS_NONE fake_swatch.mouse_filter = Control.MOUSE_FILTER_PASS fake_swatch.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + fake_swatch.tooltip_text = Translator.translate("Add new color") var color_swatch_ref := ColorSwatchScene.instantiate() fake_swatch.custom_minimum_size = color_swatch_ref.custom_minimum_size color_swatch_ref.queue_free() @@ -185,22 +186,22 @@ func move_down() -> void: layout_changed.emit() func copy_palette(palette_idx: int) -> void: - DisplayServer.clipboard_set(Configs.savedata.get_palette(palette_idx).to_text()) + DisplayServer.clipboard_set(Configs.savedata.get_palette(palette_idx).get_as_markup()) func save_palette(palette_idx: int) -> void: var saved_palette := Configs.savedata.get_palette(palette_idx) - FileUtils.open_xml_export_dialog(saved_palette.to_text(), saved_palette.title) + FileUtils.open_xml_export_dialog(saved_palette.get_as_markup(), saved_palette.title) func open_palette_options() -> void: var btn_arr: Array[Button] = [] btn_arr.append(ContextPopup.create_button("Pure", apply_preset.bind(Palette.Preset.PURE), palette.is_same_as_preset(Palette.Preset.PURE), - load("res://assets/icons/PresetPure.svg"))) + load("res://assets/icons/PresetPure.svg"), true)) btn_arr.append(ContextPopup.create_button("Grayscale", apply_preset.bind(Palette.Preset.GRAYSCALE), palette.is_same_as_preset(Palette.Preset.GRAYSCALE), - load("res://assets/icons/PresetGrayscale.svg"))) + load("res://assets/icons/PresetGrayscale.svg"), true)) btn_arr.append(ContextPopup.create_button("Empty", apply_preset.bind(Palette.Preset.EMPTY), palette.is_same_as_preset(Palette.Preset.EMPTY), diff --git a/src/ui_widgets/pathdata_field.gd b/src/ui_widgets/pathdata_field.gd index a1b3041..333797f 100644 --- a/src/ui_widgets/pathdata_field.gd +++ b/src/ui_widgets/pathdata_field.gd @@ -265,7 +265,7 @@ func _commands_draw() -> void: rect.position.x = rect.end.x + 3 draw_numfield(rect, "ry", cmd) rect.position.x = rect.end.x + 4 - draw_numfield(rect, "rot", cmd) + draw_angle_numfield(rect, cmd) rect.position.x = rect.end.x + 4 rect.size.x = 19 var flag_field := FlagFieldScene.instantiate() @@ -291,6 +291,12 @@ func _commands_draw() -> void: "H": draw_numfield(rect, "x", cmd) "V": draw_numfield(rect, "y", cmd) +func draw_angle_numfield(rect: Rect2, path_command: PathCommand) -> void: + mini_line_edit_stylebox.draw(ci, rect) + ThemeUtils.mono_font.draw_string(ci, rect.position + Vector2(3, 13), + NumstringParser.basic_num_to_text(path_command.get("rot"), true), + HORIZONTAL_ALIGNMENT_LEFT, rect.size.x - 6, mini_line_edit_font_size, mini_line_edit_font_color) + func draw_numfield(rect: Rect2, property: String, path_command: PathCommand) -> void: mini_line_edit_stylebox.draw(ci, rect) ThemeUtils.mono_font.draw_string(ci, rect.position + Vector2(3, 13), diff --git a/src/ui_widgets/preview_rect.gd b/src/ui_widgets/preview_rect.gd index 08f8907..1fc5a10 100644 --- a/src/ui_widgets/preview_rect.gd +++ b/src/ui_widgets/preview_rect.gd @@ -6,7 +6,7 @@ const MAX_IMAGE_DIMENSION = 512 @onready var texture_preview: TextureRect = $Checkerboard/TexturePreview func setup_svg_without_dimensions(svg_text: String) -> void: - setup_svg(svg_text, SVGParser.text_to_root(svg_text).svg.get_size()) + setup_svg(svg_text, SVGParser.markup_to_root(svg_text).svg.get_size()) func setup_svg(svg_text: String, dimensions: Vector2) -> void: if not is_node_ready(): diff --git a/src/ui_widgets/preview_rect.tscn b/src/ui_widgets/preview_rect.tscn index 924d21b..6b3cbdb 100644 --- a/src/ui_widgets/preview_rect.tscn +++ b/src/ui_widgets/preview_rect.tscn @@ -2,7 +2,7 @@ [ext_resource type="Script" uid="uid://cx43kir18is86" path="res://src/ui_widgets/preview_rect.gd" id="1_n2xbu"] [ext_resource type="Shader" uid="uid://i2y5pyhcgra2" path="res://src/shaders/zoom_shader.gdshader" id="2_qrqjp"] -[ext_resource type="Texture2D" uid="uid://c68og6bsqt0lb" path="res://assets/icons/backgrounds/Checkerboard.svg" id="3_tuqha"] +[ext_resource type="Texture2D" uid="uid://c68og6bsqt0lb" path="res://assets/icons/Checkerboard.svg" id="3_tuqha"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_y7eee"] shader = ExtResource("2_qrqjp") diff --git a/src/ui_widgets/setting_frame.gd b/src/ui_widgets/setting_frame.gd index 303a5bd..54feeae 100644 --- a/src/ui_widgets/setting_frame.gd +++ b/src/ui_widgets/setting_frame.gd @@ -112,7 +112,7 @@ func _on_reset_button_pressed() -> void: update_widgets() func _checkbox_modification() -> void: - setter.call(!getter.call()) + setter.call(not getter.call()) post_modification() func _color_modification(value: String, enable_alpha: bool) -> void: diff --git a/src/ui_widgets/settings_content_generic.gd b/src/ui_widgets/settings_content_generic.gd index fa745c5..1fe2f54 100644 --- a/src/ui_widgets/settings_content_generic.gd +++ b/src/ui_widgets/settings_content_generic.gd @@ -48,12 +48,23 @@ class SettingFormatterPreview: var resource_bind: Formatter var root_element: ElementRoot var show_only_children: bool - func _init(new_resource_bind: ConfigResource, - new_root_element: ElementRoot, new_show_only_children := false) -> void: + func _init(new_resource_bind: ConfigResource, new_root_element: ElementRoot, new_show_only_children := false) -> void: resource_bind = new_resource_bind root_element = new_root_element show_only_children = new_show_only_children +class SettingCanvasPreview: + var root_element: ElementRoot + var hovered_xid: PackedInt32Array + var selected_xids: Array[PackedInt32Array] + var show_grid := false + func _init(new_root_element: ElementRoot, new_hovered_xid := PackedInt32Array(), new_selected_xids: Array[PackedInt32Array] = [], + new_show_grid := true) -> void: + root_element = new_root_element + hovered_xid = new_hovered_xid + selected_xids = new_selected_xids + show_grid = new_show_grid + func _ready() -> void: Configs.language_changed.connect(setup_content) @@ -76,16 +87,11 @@ func _ready() -> void: btn.action_mode = BaseButton.ACTION_MODE_BUTTON_PRESS categories.add_child(btn) add_child(categories) - setting_container = VBoxContainer.new() - setting_container.add_theme_constant_override("separation", 6) - add_child(setting_container) categories.get_child(0).button_pressed = true - setup_content() - else: - setting_container = VBoxContainer.new() - setting_container.add_theme_constant_override("separation", 6) - add_child(setting_container) - setup_content() + setting_container = VBoxContainer.new() + setting_container.add_theme_constant_override("separation", 6) + add_child(setting_container) + setup_content() func _get_current_setup_resource() -> ConfigResource: return current_setup_resources[current_setup_resource_index] @@ -108,6 +114,7 @@ func setup_content() -> void: SettingsMenu.TabIndex.THEMING: setup_theming_content() SettingsMenu.TabIndex.TAB_BAR: setup_tab_bar_content() SettingsMenu.TabIndex.OTHER: setup_other_content() + HandlerGUI.throw_mouse_motion_event() func setup_formatting_content() -> void: var current_setup_resource: Formatter = _get_current_setup_resource() @@ -131,8 +138,7 @@ func setup_formatting_content() -> void: xml_keep_comments_circle_element.set_attribute("r", 4) xml_keep_comments_circle_element.set_attribute("fill", "gold") xml_keep_comments_root_element.insert_child(1, xml_keep_comments_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_keep_comments_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_keep_comments_root_element, true)) current_setup_setting = "xml_add_trailing_newline" add_checkbox(Translator.translate("Add trailing newline")) @@ -145,10 +151,8 @@ func setup_formatting_content() -> void: xml_add_trailing_newline_circle_element.set_attribute("cy", 8) xml_add_trailing_newline_circle_element.set_attribute("r", 4) xml_add_trailing_newline_circle_element.set_attribute("fill", "gold") - xml_add_trailing_newline_root_element.insert_child(0, - xml_add_trailing_newline_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_add_trailing_newline_root_element)) + xml_add_trailing_newline_root_element.insert_child(0, xml_add_trailing_newline_circle_element) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_add_trailing_newline_root_element)) current_setup_setting = "xml_shorthand_tags" add_dropdown(Translator.translate("Use shorthand tag syntax"), @@ -168,8 +172,7 @@ func setup_formatting_content() -> void: xml_shorthand_tags_circle_element.set_attribute("fill", "url(#a)") xml_shorthand_tags_root_element.insert_child(0, xml_shorthand_tags_linear_gradient_element) xml_shorthand_tags_root_element.insert_child(1, xml_shorthand_tags_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_shorthand_tags_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_shorthand_tags_root_element, true)) current_setup_setting = "xml_shorthand_tags_space_out_slash" add_checkbox(Translator.translate("Space out the slash of shorthand tags")) @@ -178,10 +181,8 @@ func setup_formatting_content() -> void: xml_shorthand_tags_space_out_slash_circle_element.set_attribute("cx", 6) xml_shorthand_tags_space_out_slash_circle_element.set_attribute("cy", 8) xml_shorthand_tags_space_out_slash_circle_element.set_attribute("r", 4) - xml_shorthand_tags_space_out_slash_root_element.insert_child(0, - xml_shorthand_tags_space_out_slash_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_shorthand_tags_space_out_slash_root_element, true)) + xml_shorthand_tags_space_out_slash_root_element.insert_child(0, xml_shorthand_tags_space_out_slash_circle_element) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_shorthand_tags_space_out_slash_root_element, true)) current_setup_setting = "xml_pretty_formatting" add_checkbox(Translator.translate("Use pretty formatting")) @@ -207,12 +208,10 @@ func setup_formatting_content() -> void: xml_pretty_formatting_circle_element.set_attribute("fill", "url(#a)") xml_pretty_formatting_root_element.insert_child(0, xml_pretty_formatting_linear_gradient_element) xml_pretty_formatting_root_element.insert_child(1, xml_pretty_formatting_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_pretty_formatting_root_element)) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_pretty_formatting_root_element)) current_setup_setting = "xml_indentation_use_spaces" - add_checkbox(Translator.translate("Use spaces instead of tabs"), - not current_setup_resource.xml_pretty_formatting) + add_checkbox(Translator.translate("Use spaces instead of tabs"), not current_setup_resource.xml_pretty_formatting) add_preview(SettingTextPreview.new(Translator.translate( "When enabled, uses spaces instead of a single tab for indentation."), SettingTextPreview.get_no_effect_in_configuration_warning( @@ -229,10 +228,8 @@ func setup_formatting_content() -> void: xml_indentation_spaces_circle_element.set_attribute("cy", 8) xml_indentation_spaces_circle_element.set_attribute("r", 4) xml_indentation_spaces_circle_element.set_attribute("fill", "gold") - xml_indentation_spaces_root_element.insert_child(0, - xml_indentation_spaces_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - xml_indentation_spaces_root_element, false)) + xml_indentation_spaces_root_element.insert_child(0, xml_indentation_spaces_circle_element) + add_preview(SettingFormatterPreview.new(current_setup_resource, xml_indentation_spaces_root_element, false)) else: add_preview(SettingTextPreview.new("", SettingTextPreview.WarningType.NO_EFFECT_IN_CURRENT_CONFIGURATION)) @@ -246,10 +243,8 @@ func setup_formatting_content() -> void: number_remove_leading_zero_circle_element.set_attribute("cy", -0.8) number_remove_leading_zero_circle_element.set_attribute("r", 1.6) number_remove_leading_zero_circle_element.set_attribute("fill", "gold") - number_remove_leading_zero_root_element.insert_child(0, - number_remove_leading_zero_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - number_remove_leading_zero_root_element, true)) + number_remove_leading_zero_root_element.insert_child(0, number_remove_leading_zero_circle_element) + add_preview(SettingFormatterPreview.new(current_setup_resource, number_remove_leading_zero_root_element, true)) current_setup_setting = "number_use_exponent_if_shorter" add_checkbox(Translator.translate("Use exponential when shorter")) @@ -259,10 +254,8 @@ func setup_formatting_content() -> void: number_use_exponent_if_shorter_circle_element.set_attribute("cy", -0.005) number_use_exponent_if_shorter_circle_element.set_attribute("r", 2000) number_use_exponent_if_shorter_circle_element.set_attribute("fill", "gold") - number_use_exponent_if_shorter_root_element.insert_child(0, - number_use_exponent_if_shorter_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - number_use_exponent_if_shorter_root_element, true)) + number_use_exponent_if_shorter_root_element.insert_child(0, number_use_exponent_if_shorter_circle_element) + add_preview(SettingFormatterPreview.new(current_setup_resource, number_use_exponent_if_shorter_root_element, true)) add_section(Translator.translate("Colors")) current_setup_setting = "color_use_named_colors" @@ -283,8 +276,7 @@ func setup_formatting_content() -> void: color_use_named_colors_circle_2.set_attribute("r", 2) color_use_named_colors_circle_2.set_attribute("fill", "turquoise") color_use_named_colors_root_element.insert_child(1, color_use_named_colors_circle_2) - add_preview(SettingFormatterPreview.new(current_setup_resource, - color_use_named_colors_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, color_use_named_colors_root_element, true)) current_setup_setting = "color_primary_syntax" add_dropdown(Translator.translate("Primary syntax"), @@ -298,8 +290,7 @@ func setup_formatting_content() -> void: color_primary_syntax_circle_element.set_attribute("fill", "#d4b") color_primary_syntax_circle_element.set_attribute("stroke", "#f4c4d3") color_primary_syntax_root_element.insert_child(0, color_primary_syntax_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - color_primary_syntax_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, color_primary_syntax_root_element, true)) current_setup_setting = "color_capital_hex" add_checkbox(Translator.translate("Capitalize hexadecimal letters"), @@ -312,11 +303,9 @@ func setup_formatting_content() -> void: color_capital_hex_circle_element.set_attribute("r", 4) color_capital_hex_circle_element.set_attribute("fill", "#decade") color_capital_hex_root_element.insert_child(0, color_capital_hex_circle_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - color_capital_hex_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, color_capital_hex_root_element, true)) else: - add_preview(SettingTextPreview.new("", - SettingTextPreview.WarningType.NO_EFFECT_IN_CURRENT_CONFIGURATION)) + add_preview(SettingTextPreview.new("", SettingTextPreview.WarningType.NO_EFFECT_IN_CURRENT_CONFIGURATION)) add_section(Translator.translate("Pathdata")) current_setup_setting = "pathdata_compress_numbers" @@ -326,8 +315,7 @@ func setup_formatting_content() -> void: pathdata_compress_numbers_path_element.set_attribute("d", "m 4 6.5 l 0.5 -0.8 v 2 z") pathdata_compress_numbers_path_element.set_attribute("fill", "gold") pathdata_compress_numbers_root_element.insert_child(0, pathdata_compress_numbers_path_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - pathdata_compress_numbers_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, pathdata_compress_numbers_root_element, true)) current_setup_setting = "pathdata_minimize_spacing" add_checkbox(Translator.translate("Minimize spacing")) @@ -336,8 +324,7 @@ func setup_formatting_content() -> void: pathdata_minimize_spacing_path_element.set_attribute("d", "m 4 6.5 l 0.5 -0.8 v 2 z") pathdata_minimize_spacing_path_element.set_attribute("fill", "gold") pathdata_minimize_spacing_root_element.insert_child(0, pathdata_minimize_spacing_path_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - pathdata_minimize_spacing_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, pathdata_minimize_spacing_root_element, true)) current_setup_setting = "pathdata_remove_spacing_after_flags" add_checkbox(Translator.translate("Remove spacing after flags")) @@ -346,19 +333,16 @@ func setup_formatting_content() -> void: pathdata_remove_spacing_after_flags_path_element.set_attribute("d", "m 1 3.5 a 2 3 0 1 0 4 2 z") pathdata_remove_spacing_after_flags_path_element.set_attribute("fill", "gold") pathdata_remove_spacing_after_flags_root_element.insert_child(0, pathdata_remove_spacing_after_flags_path_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - pathdata_remove_spacing_after_flags_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, pathdata_remove_spacing_after_flags_root_element, true)) current_setup_setting = "pathdata_remove_consecutive_commands" add_checkbox(Translator.translate("Remove consecutive commands")) var pathdata_remove_consecutive_commands_root_element := ElementRoot.new() var pathdata_remove_consecutive_commands_path_element := ElementPath.new() - pathdata_remove_consecutive_commands_path_element.set_attribute("d", - "m 4 6.5 l -1 2 l 2.5 1 q 3.5 -1 2 -2 q -1.5 0.5 -1 -2 z") + pathdata_remove_consecutive_commands_path_element.set_attribute("d", "m 4 6.5 l -1 2 l 2.5 1 q 3.5 -1 2 -2 q -1.5 0.5 -1 -2 z") pathdata_remove_consecutive_commands_path_element.set_attribute("fill", "gold") pathdata_remove_consecutive_commands_root_element.insert_child(0, pathdata_remove_consecutive_commands_path_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - pathdata_remove_consecutive_commands_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, pathdata_remove_consecutive_commands_root_element, true)) add_section(Translator.translate("Transform lists")) current_setup_setting = "transform_list_compress_numbers" @@ -367,11 +351,9 @@ func setup_formatting_content() -> void: var transform_list_compress_numbers_polygon_element := ElementPolygon.new() transform_list_compress_numbers_polygon_element.set_attribute("points", "2 4 5 8 9 1") transform_list_compress_numbers_polygon_element.set_attribute("fill", "gold") - transform_list_compress_numbers_polygon_element.set_attribute("transform", - "rotate(7.5 -0.5 0.8)") + transform_list_compress_numbers_polygon_element.set_attribute("transform", "rotate(7.5 -0.5 0.8)") transform_list_compress_numbers_root_element.insert_child(0, transform_list_compress_numbers_polygon_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - transform_list_compress_numbers_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, transform_list_compress_numbers_root_element, true)) current_setup_setting = "transform_list_minimize_spacing" add_checkbox(Translator.translate("Minimize spacing")) @@ -379,11 +361,9 @@ func setup_formatting_content() -> void: var transform_list_minimize_spacing_polygon_element := ElementPolygon.new() transform_list_minimize_spacing_polygon_element.set_attribute("points", "2 4 5 8 9 1") transform_list_minimize_spacing_polygon_element.set_attribute("fill", "gold") - transform_list_minimize_spacing_polygon_element.set_attribute("transform", - "rotate(7.5 -0.5 0.8)") + transform_list_minimize_spacing_polygon_element.set_attribute("transform", "rotate(7.5 -0.5 0.8)") transform_list_minimize_spacing_root_element.insert_child(0, transform_list_minimize_spacing_polygon_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - transform_list_minimize_spacing_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, transform_list_minimize_spacing_root_element, true)) current_setup_setting = "transform_list_remove_unnecessary_params" add_checkbox(Translator.translate("Remove unnecessary parameters")) @@ -391,11 +371,9 @@ func setup_formatting_content() -> void: var transform_list_remove_unnecessary_params_polygon_element := ElementPolygon.new() transform_list_remove_unnecessary_params_polygon_element.set_attribute("points", "2 4 5 8 9 1") transform_list_remove_unnecessary_params_polygon_element.set_attribute("fill", "gold") - transform_list_remove_unnecessary_params_polygon_element.set_attribute("transform", - "scale(2 2) translate(4 0) rotate(30 0 0)") + transform_list_remove_unnecessary_params_polygon_element.set_attribute("transform", "scale(2 2) translate(4 0) rotate(30 0 0)") transform_list_remove_unnecessary_params_root_element.insert_child(0, transform_list_remove_unnecessary_params_polygon_element) - add_preview(SettingFormatterPreview.new(current_setup_resource, - transform_list_remove_unnecessary_params_root_element, true)) + add_preview(SettingFormatterPreview.new(current_setup_resource, transform_list_remove_unnecessary_params_root_element, true)) func setup_theming_content() -> void: var current_setup_resource := Configs.savedata @@ -405,8 +383,8 @@ func setup_theming_content() -> void: current_setup_resource.reset_theme_items_to_default, SaveData.ThemePreset.size(), SaveData.get_theme_preset_value_text_map(), current_setup_resource.is_theming_default) - add_preview(SettingTextPreview.new( - Translator.translate("Determines the default values of theming-related settings, including the highlighter preset."))) + add_preview(SettingTextPreview.new(Translator.translate( + "Determines the default values of theming-related settings, including the highlighter preset."))) add_section(Translator.translate("Primary theme colors")) current_setup_setting = "base_color" @@ -414,6 +392,10 @@ func setup_theming_content() -> void: current_setup_setting = "accent_color" add_color_edit(Translator.translate("Accent color"), false) + var basic_svg_text = """""" + var basic_svg_text_with_syntax_error = """ Basic text & " ' ]]> """ + add_section(Translator.translate("SVG Text colors")) current_setup_setting = "highlighter_preset" add_profile_picker(Translator.translate("Highlighter preset"), @@ -425,52 +407,82 @@ func setup_theming_content() -> void: Translator.translate("Determines the default values of SVG highlighter settings."))) current_setup_setting = "highlighting_symbol_color" add_color_edit(Translator.translate("Symbol color")) - add_preview(SettingCodePreview.new( - """""")) + add_preview(SettingCodePreview.new(basic_svg_text)) current_setup_setting = "highlighting_element_color" add_color_edit(Translator.translate("Element color")) - add_preview(SettingCodePreview.new( - """""")) + add_preview(SettingCodePreview.new(basic_svg_text)) current_setup_setting = "highlighting_attribute_color" add_color_edit(Translator.translate("Attribute color")) - add_preview(SettingCodePreview.new( - """""")) + add_preview(SettingCodePreview.new(basic_svg_text)) current_setup_setting = "highlighting_string_color" add_color_edit(Translator.translate("String color")) - add_preview(SettingCodePreview.new( - """""")) + add_preview(SettingCodePreview.new(basic_svg_text)) current_setup_setting = "highlighting_comment_color" add_color_edit(Translator.translate("Comment color")) - add_preview(SettingCodePreview.new( - """ Basic text & " ' ]]> """)) + add_preview(SettingCodePreview.new(fancy_svg_text)) current_setup_setting = "highlighting_text_color" add_color_edit(Translator.translate("Text color")) - add_preview(SettingCodePreview.new( - """ Basic text & " ' ]]> """)) + add_preview(SettingCodePreview.new(fancy_svg_text)) current_setup_setting = "highlighting_cdata_color" add_color_edit(Translator.translate("CDATA color")) - add_preview(SettingCodePreview.new( - """ Basic text & " ' ]]> """)) + add_preview(SettingCodePreview.new(fancy_svg_text)) current_setup_setting = "highlighting_error_color" add_color_edit(Translator.translate("Error color")) - add_preview(SettingCodePreview.new( - """ void: [0.0, 10.0, 20.0, 30.0, 50.0, 80.0, 130.0], false, false, -SaveData.MAX_SELECTION_RECTANGLE_SPEED, SaveData.MAX_SELECTION_RECTANGLE_SPEED) + add_preview(SettingCanvasPreview.new(selection_rectangle_presentation_root, PackedInt32Array(), [PackedInt32Array([0])])) current_setup_setting = "selection_rectangle_width" add_number_dropdown(Translator.translate("Width"), [1.0, 2.0, 3.0, 4.0], false, false, 1.0, SaveData.MAX_SELECTION_RECTANGLE_WIDTH) + add_preview(SettingCanvasPreview.new(selection_rectangle_presentation_root, PackedInt32Array(), [PackedInt32Array([0])])) current_setup_setting = "selection_rectangle_dash_length" add_number_dropdown(Translator.translate("Dash length"), [5.0, 10.0, 15.0, 20.0], false, false, 1.0, SaveData.MAX_SELECTION_RECTANGLE_DASH_LENGTH) + add_preview(SettingCanvasPreview.new(selection_rectangle_presentation_root, PackedInt32Array(), [PackedInt32Array([0])])) current_setup_setting = "selection_rectangle_color1" add_color_edit(Translator.translate("Color {index}").format({"index": "1"})) + add_preview(SettingCanvasPreview.new(selection_rectangle_presentation_root, PackedInt32Array(), [PackedInt32Array([0])])) current_setup_setting = "selection_rectangle_color2" add_color_edit(Translator.translate("Color {index}").format({"index": "2"})) + add_preview(SettingCanvasPreview.new(selection_rectangle_presentation_root, PackedInt32Array(), [PackedInt32Array([0])])) add_section(Translator.translate("Basic colors")) current_setup_setting = "canvas_color" add_color_edit(Translator.translate("Canvas color"), false) + add_preview(SettingCanvasPreview.new(empty_preview_root)) current_setup_setting = "grid_color" add_color_edit(Translator.translate("Grid color"), false) - + add_preview(SettingCanvasPreview.new(empty_preview_root)) current_setup_setting = "basic_color_valid" add_color_edit(Translator.translate("Valid color")) - add_preview(SettingBasicColorPreview.new(current_setup_setting, - Translator.translate("Valid color"))) - + add_preview(SettingBasicColorPreview.new(current_setup_setting, Translator.translate("Valid color"))) current_setup_setting = "basic_color_error" add_color_edit(Translator.translate("Error color")) - add_preview(SettingBasicColorPreview.new(current_setup_setting, - Translator.translate("Error color"))) - + add_preview(SettingBasicColorPreview.new(current_setup_setting, Translator.translate("Error color"))) current_setup_setting = "basic_color_warning" add_color_edit(Translator.translate("Warning color")) - add_preview(SettingBasicColorPreview.new(current_setup_setting, - Translator.translate("Warning color"))) + add_preview(SettingBasicColorPreview.new(current_setup_setting, Translator.translate("Warning color"))) func setup_tab_bar_content() -> void: add_section(Translator.translate("Input")) @@ -659,8 +672,7 @@ value_text_map: Dictionary) -> Control: # Dictionary[Variant, String] frame.setup_dropdown(values, value_text_map) add_frame(frame) # Some checkboxes need to update the dimness of the text of other settings. - # There's no continuous editing with checkboxes, so it's safe to just rebuild - # the content for them. + # There's no continuous editing with checkboxes, so it's safe to just rebuild the content for them. frame.value_changed.connect(setup_content) return frame @@ -698,8 +710,17 @@ func setup_frame(frame: Control, has_default := true) -> void: frame.getter = resource_ref.get.bind(bind) if has_default: frame.default = resource_ref.get_setting_default(current_setup_setting) - frame.focus_entered.connect(set_preview.bind(current_setup_setting)) - frame.focus_exited.connect(remove_preview.bind(current_setup_setting)) + frame.mouse_entered.connect(set_preview.bind(current_setup_setting)) + # Remove the preview when one of the following happens: + # 1. The mouse exits the setting frame and there's no popup. + # 2. The popup after the mouse exited the setting frame (i.e. color picker or dropdown) has been cleared. + frame.mouse_exited.connect( + func() -> void: + if HandlerGUI.popup_stack.is_empty(): + remove_preview(bind) + else: + HandlerGUI.popups_cleared.connect(remove_preview.bind(bind), CONNECT_ONE_SHOT) + ) func add_frame(frame: Control) -> void: if setting_container.get_child_count() > 0: @@ -722,7 +743,7 @@ func remove_preview(setting: String) -> void: func emit_preview_changed() -> void: if current_previewed_setting.is_empty() or not previews.has(current_previewed_setting): - preview_changed.emit(Control.new()) + preview_changed.emit(null) return var preview := previews[current_previewed_setting] @@ -811,9 +832,9 @@ func emit_preview_changed() -> void: var update_text := func() -> void: if preview.show_only_children: - code_preview.text = SVGParser.root_children_to_text(preview.root_element, preview.resource_bind) + code_preview.text = SVGParser.root_children_to_markup(preview.root_element, preview.resource_bind) else: - code_preview.text = SVGParser.root_to_text(preview.root_element, preview.resource_bind) + code_preview.text = SVGParser.root_to_markup(preview.root_element, preview.resource_bind) code_preview.add_theme_color_override("font_readonly_color", Color.WHITE) var text_edit_default_stylebox := code_preview.get_theme_stylebox("normal") @@ -825,11 +846,16 @@ func emit_preview_changed() -> void: code_preview.add_theme_stylebox_override("normal", empty_stylebox) code_preview.add_theme_stylebox_override("read_only", empty_stylebox) Configs.highlighting_colors_changed.connect(update_highlighter) - code_preview.tree_exiting.connect(Configs.highlighting_colors_changed.disconnect.bind( - update_highlighter), CONNECT_ONE_SHOT) + code_preview.tree_exiting.connect(Configs.highlighting_colors_changed.disconnect.bind(update_highlighter), CONNECT_ONE_SHOT) update_highlighter.call() preview.resource_bind.changed_deferred.connect(update_text) - code_preview.tree_exiting.connect(preview.resource_bind.changed_deferred.disconnect.bind( - update_text), CONNECT_ONE_SHOT) + code_preview.tree_exiting.connect(preview.resource_bind.changed_deferred.disconnect.bind(update_text), CONNECT_ONE_SHOT) update_text.call() preview_changed.emit(code_preview) + elif preview is SettingCanvasPreview: + var canvas := Canvas.new() + canvas.root_element = preview.root_element + canvas.hovered_xid = preview.hovered_xid + canvas.selected_xids = preview.selected_xids + canvas.show_grid = preview.show_grid + preview_changed.emit(canvas) diff --git a/src/ui_widgets/settings_content_palettes.gd b/src/ui_widgets/settings_content_palettes.gd index 737546a..d85dd0b 100644 --- a/src/ui_widgets/settings_content_palettes.gd +++ b/src/ui_widgets/settings_content_palettes.gd @@ -34,7 +34,7 @@ func _popup_xml_palette_options(palette_xml_button: Button) -> void: btn_arr.append(ContextPopup.create_button(Translator.translate("Import XML"), add_imported_palette, false, load("res://assets/icons/Import.svg"))) btn_arr.append(ContextPopup.create_button(Translator.translate("Paste XML"), - add_pasted_palette, !Palette.is_valid_palette(Utils.get_clipboard_web_safe()), + add_pasted_palette, not Palette.is_valid_palette(Utils.get_clipboard_web_safe()), load("res://assets/icons/Paste.svg"))) var context_popup := ContextPopup.new() diff --git a/src/ui_widgets/transform_field.gd b/src/ui_widgets/transform_field.gd index 5eed897..d6bc4a1 100644 --- a/src/ui_widgets/transform_field.gd +++ b/src/ui_widgets/transform_field.gd @@ -35,7 +35,7 @@ func sync_localization() -> void: placeholder_text = Translator.translate("No transforms") func setup_font(new_text: String) -> void: - use_mono_font = !new_text.is_empty() + use_mono_font = not new_text.is_empty() func sync() -> void: text = element.get_attribute_value(attribute_name) diff --git a/src/ui_widgets/viewport_controls.gd b/src/ui_widgets/viewport_controls.gd new file mode 100644 index 0000000..00c6025 --- /dev/null +++ b/src/ui_widgets/viewport_controls.gd @@ -0,0 +1,82 @@ +extends SubViewport + +var canvas: Canvas + +# Holds zoom position for Ctrl + MMB zooming. +var _zoom_to: Vector2 + +func _unhandled_input(event: InputEvent) -> void: + if HandlerGUI.get_viewport().gui_is_dragging(): + return + + if event is InputEventMouseMotion and event.button_mask & (MOUSE_BUTTON_MASK_LEFT | MOUSE_BUTTON_MASK_MIDDLE): + # Zooming with Ctrl + MMB. + if event.ctrl_pressed and event.button_mask == MOUSE_BUTTON_MASK_MIDDLE: + if _zoom_to == Vector2.ZERO: # Set zoom position if starting action. + _zoom_to = get_mouse_position() / canvas.size + var relative_y: float = canvas.wrap_mouse(event.relative).y if Configs.savedata.wraparound_panning else event.relative.y + var new_zoom := canvas.camera_zoom * (1.0 + (1 if Configs.savedata.invert_zoom else -1) * relative_y / 128.0) + canvas.set_zoom(new_zoom, _zoom_to) + else: + # Panning with LMB or MMB. This gives a reliable way to adjust the view without dragging the things on it. + var relative: Vector2 = canvas.wrap_mouse(event.relative) if Configs.savedata.wraparound_panning else event.relative + canvas.set_view(canvas.camera_center - relative / canvas.camera_zoom) + + elif event is InputEventPanGesture and DisplayServer.get_name() != "Android": + if event.ctrl_pressed: + # Zooming with Ctrl + touch? + canvas.set_zoom(canvas.camera_zoom * (1.0 + event.delta.y / 2.0)) + else: + # Panning with touch. + canvas.set_view(canvas.camera_center + event.delta * 32.0 / canvas.camera_zoom) + elif event is InputEventMagnifyGesture: + # Zooming with touch. + canvas.set_zoom(canvas.camera_zoom * event.factor) + elif event is InputEventMouseButton and event.is_pressed(): + # Actions with scrolling. + var move_vec := Vector2.ZERO + var zoom_direction := 0 + + if (not event.ctrl_pressed and not event.shift_pressed and not Configs.savedata.use_ctrl_for_zoom) or\ + (event.ctrl_pressed and Configs.savedata.use_ctrl_for_zoom): + # Zooming with scrolling. + match event.button_index: + MOUSE_BUTTON_WHEEL_UP: zoom_direction = -1 if Configs.savedata.invert_zoom else 1 + MOUSE_BUTTON_WHEEL_DOWN: zoom_direction = 1 if Configs.savedata.invert_zoom else -1 + _: return + elif event.shift_pressed: + # Inverted panning with Shift + scrolling. + match event.button_index: + MOUSE_BUTTON_WHEEL_UP: move_vec = Vector2.LEFT + MOUSE_BUTTON_WHEEL_DOWN: move_vec = Vector2.RIGHT + MOUSE_BUTTON_WHEEL_LEFT: move_vec = Vector2.UP + MOUSE_BUTTON_WHEEL_RIGHT: move_vec = Vector2.DOWN + _: return + else: + # Panning with scrolling. + match event.button_index: + MOUSE_BUTTON_WHEEL_UP: move_vec = Vector2.UP + MOUSE_BUTTON_WHEEL_DOWN: move_vec = Vector2.DOWN + MOUSE_BUTTON_WHEEL_LEFT: move_vec = Vector2.LEFT + MOUSE_BUTTON_WHEEL_RIGHT: move_vec = Vector2.RIGHT + _: return + + var mouse_offset := get_mouse_position() / canvas.size + # Apply scroll data from above. + var factor: float = event.factor + if factor == roundf(factor): # Detects if precise factor is unsupported. + factor = 1.0 + + var multiplied_factor := sqrt(2) * factor + if zoom_direction == 1: + canvas.set_zoom(canvas.camera_zoom * multiplied_factor, mouse_offset) + elif zoom_direction == -1: + canvas.set_zoom(canvas.camera_zoom / multiplied_factor, mouse_offset) + + canvas.set_view(canvas.camera_center + move_vec * factor / canvas.camera_zoom * 32) + + else: + if not event.is_echo(): + # Filter out fake mouse movement events. + if not (event is InputEventMouseMotion and event.relative == Vector2.ZERO): + _zoom_to = Vector2.ZERO # Reset Ctrl + MMB zoom position if released. diff --git a/src/ui_widgets/viewport_controls.gd.uid b/src/ui_widgets/viewport_controls.gd.uid new file mode 100644 index 0000000..28b4941 --- /dev/null +++ b/src/ui_widgets/viewport_controls.gd.uid @@ -0,0 +1 @@ +uid://b1xmxjs271ssw diff --git a/src/ui_widgets/zoom_menu.gd b/src/ui_widgets/zoom_menu.gd deleted file mode 100644 index 4dfd987..0000000 --- a/src/ui_widgets/zoom_menu.gd +++ /dev/null @@ -1,82 +0,0 @@ -extends HBoxContainer - -const MIN_ZOOM = 0.125 -const MAX_ZOOM = 512.0 - -signal zoom_changed(zoom_level: float, offset: Vector2) -signal zoom_reset_pressed - -@onready var zoom_out_button: BetterButton = $ZoomOut -@onready var zoom_in_button: BetterButton = $ZoomIn -@onready var zoom_reset_button: BetterButton = $ZoomReset - -var _zoom_level: float - - -func _ready() -> void: - var shortcuts := ShortcutsRegistration.new() - shortcuts.add_shortcut("zoom_in", zoom_in) - shortcuts.add_shortcut("zoom_out", zoom_out) - shortcuts.add_shortcut("zoom_reset", zoom_reset) - HandlerGUI.register_shortcuts(self, shortcuts) - zoom_out_button.shortcuts_bind = shortcuts - zoom_in_button.shortcuts_bind = shortcuts - zoom_reset_button.shortcuts_bind = shortcuts - - -func set_zoom(new_value: float, offset := Vector2(0.5, 0.5)) -> void: - new_value = clampf(new_value, MIN_ZOOM, MAX_ZOOM) - if _zoom_level != new_value: - _zoom_level = new_value - zoom_changed.emit(_zoom_level, offset) - update_buttons_appearance() - -func zoom_out(factor := 1.0, offset := Vector2(0.5, 0.5)) -> void: - if factor == 1.0: - set_zoom(_zoom_level / sqrt(2), offset) - else: - set_zoom(_zoom_level / (factor + 1), offset) - -func zoom_in(factor := 1.0, offset := Vector2(0.5, 0.5)) -> void: - if factor == 1.0: - set_zoom(_zoom_level * sqrt(2), offset) - else: - set_zoom(_zoom_level * (factor + 1), offset) - -# This needs a custom implementation to whatever is listening to the signal. -func zoom_reset() -> void: - zoom_reset_pressed.emit() - - -func update_buttons_appearance() -> void: - if not zoom_reset_button: - return - if _zoom_level < 0.1: - zoom_reset_button.text = Utils.num_simple(_zoom_level * 100, 2) + "%" - elif _zoom_level < 10.0: - zoom_reset_button.text = Utils.num_simple(_zoom_level * 100, 1) + "%" - elif _zoom_level < 100.0: - zoom_reset_button.text = String.num_uint64(roundi(_zoom_level * 100)) + "%" - else: - zoom_reset_button.text = Utils.num_simple(_zoom_level, 1) + "x" - - var is_max_zoom := _zoom_level > MAX_ZOOM or is_equal_approx(_zoom_level, MAX_ZOOM) - var is_min_zoom := _zoom_level < MIN_ZOOM or is_equal_approx(_zoom_level, MIN_ZOOM) - - zoom_in_button.disabled = is_max_zoom - zoom_in_button.mouse_default_cursor_shape = Control.CURSOR_ARROW if is_max_zoom else Control.CURSOR_POINTING_HAND - - zoom_out_button.disabled = is_min_zoom - zoom_out_button.mouse_default_cursor_shape = Control.CURSOR_ARROW if is_min_zoom else Control.CURSOR_POINTING_HAND - - -func _on_zoom_out_pressed() -> void: - HandlerGUI.throw_action_event("zoom_out") - - -func _on_zoom_reset_pressed() -> void: - HandlerGUI.throw_action_event("zoom_reset") - - -func _on_zoom_in_pressed() -> void: - HandlerGUI.throw_action_event("zoom_in") diff --git a/src/ui_widgets/zoom_widget.gd b/src/ui_widgets/zoom_widget.gd new file mode 100644 index 0000000..74cf335 --- /dev/null +++ b/src/ui_widgets/zoom_widget.gd @@ -0,0 +1,47 @@ +extends HBoxContainer + +var min_zoom: float +var max_zoom: float + +signal zoom_in_pressed +signal zoom_out_pressed +signal zoom_reset_pressed + +@onready var zoom_out_button: BetterButton = $ZoomOut +@onready var zoom_in_button: BetterButton = $ZoomIn +@onready var zoom_reset_button: BetterButton = $ZoomReset + + +func _ready() -> void: + var shortcuts := ShortcutsRegistration.new() + shortcuts.add_shortcut("zoom_in", zoom_in_pressed.emit) + shortcuts.add_shortcut("zoom_out", zoom_out_pressed.emit) + shortcuts.add_shortcut("zoom_reset", zoom_reset_pressed.emit) + HandlerGUI.register_shortcuts(self, shortcuts) + zoom_out_button.shortcuts_bind = shortcuts + zoom_in_button.shortcuts_bind = shortcuts + zoom_reset_button.shortcuts_bind = shortcuts + + +func setup_limits(new_min_zoom: float, new_max_zoom: float) -> void: + min_zoom = new_min_zoom + max_zoom = new_max_zoom + +func sync_to_value(new_zoom: float) -> void: + new_zoom = clampf(new_zoom, min_zoom, max_zoom) + if new_zoom < 0.1: + zoom_reset_button.text = Utils.num_simple(new_zoom * 100, 2) + "%" + elif new_zoom < 10.0: + zoom_reset_button.text = Utils.num_simple(new_zoom * 100, 1) + "%" + elif new_zoom < 100.0: + zoom_reset_button.text = String.num_uint64(roundi(new_zoom * 100)) + "%" + else: + zoom_reset_button.text = Utils.num_simple(new_zoom, 1) + "x" + + var is_max_zoom := is_equal_approx(new_zoom, max_zoom) + zoom_in_button.disabled = is_max_zoom + zoom_in_button.mouse_default_cursor_shape = Control.CURSOR_ARROW if is_max_zoom else Control.CURSOR_POINTING_HAND + + var is_min_zoom := is_equal_approx(new_zoom, min_zoom) + zoom_out_button.disabled = is_min_zoom + zoom_out_button.mouse_default_cursor_shape = Control.CURSOR_ARROW if is_min_zoom else Control.CURSOR_POINTING_HAND diff --git a/src/ui_widgets/zoom_menu.gd.uid b/src/ui_widgets/zoom_widget.gd.uid similarity index 100% rename from src/ui_widgets/zoom_menu.gd.uid rename to src/ui_widgets/zoom_widget.gd.uid diff --git a/src/ui_widgets/zoom_menu.tscn b/src/ui_widgets/zoom_widget.tscn similarity index 76% rename from src/ui_widgets/zoom_menu.tscn rename to src/ui_widgets/zoom_widget.tscn index 91ddd5a..51a1747 100644 --- a/src/ui_widgets/zoom_menu.tscn +++ b/src/ui_widgets/zoom_widget.tscn @@ -1,7 +1,7 @@ -[gd_scene load_steps=5 format=3 uid="uid://oltvrf01xrxl"] +[gd_scene load_steps=5 format=3 uid="uid://ywqwxvmepuji"] [ext_resource type="Texture2D" uid="uid://c2h5snkvemm4p" path="res://assets/icons/Minus.svg" id="1_8ggy2"] -[ext_resource type="Script" uid="uid://dj2q7wnto3uqp" path="res://src/ui_widgets/zoom_menu.gd" id="1_18ab8"] +[ext_resource type="Script" uid="uid://dj2q7wnto3uqp" path="res://src/ui_widgets/zoom_widget.gd" id="1_qudrd"] [ext_resource type="Texture2D" uid="uid://eif2ioi0mw17" path="res://assets/icons/Plus.svg" id="2_284x5"] [ext_resource type="Script" uid="uid://ynx3s1jc6bwq" path="res://src/ui_widgets/BetterButton.gd" id="3_r0sro"] @@ -10,7 +10,7 @@ anchors_preset = -1 offset_right = 114.0 offset_bottom = 24.0 alignment = 1 -script = ExtResource("1_18ab8") +script = ExtResource("1_qudrd") [node name="ZoomOut" type="Button" parent="."] layout_mode = 2 @@ -19,6 +19,7 @@ mouse_default_cursor_shape = 2 icon = ExtResource("1_8ggy2") icon_alignment = 1 script = ExtResource("3_r0sro") +action = "zoom_out" metadata/_custom_type_script = "uid://ynx3s1jc6bwq" [node name="ZoomReset" type="Button" parent="."] @@ -28,6 +29,7 @@ focus_mode = 0 mouse_default_cursor_shape = 2 text = "100%" script = ExtResource("3_r0sro") +action = "zoom_reset" metadata/_custom_type_script = "uid://ynx3s1jc6bwq" [node name="ZoomIn" type="Button" parent="."] @@ -37,8 +39,5 @@ mouse_default_cursor_shape = 2 icon = ExtResource("2_284x5") icon_alignment = 1 script = ExtResource("3_r0sro") +action = "zoom_in" metadata/_custom_type_script = "uid://ynx3s1jc6bwq" - -[connection signal="pressed" from="ZoomOut" to="." method="_on_zoom_out_pressed"] -[connection signal="pressed" from="ZoomReset" to="." method="_on_zoom_reset_pressed"] -[connection signal="pressed" from="ZoomIn" to="." method="_on_zoom_in_pressed"] diff --git a/src/utils/AttributeFieldBuilder.gd b/src/utils/AttributeFieldBuilder.gd index 6c0d78c..d210e1f 100644 --- a/src/utils/AttributeFieldBuilder.gd +++ b/src/utils/AttributeFieldBuilder.gd @@ -17,7 +17,7 @@ static func create(attribute: String, element: Element) -> Control: DB.AttributeType.COLOR: return _generate(ColorFieldScene, element, attribute) DB.AttributeType.ENUM: return _generate(EnumFieldScene, element, attribute) DB.AttributeType.NUMERIC: - match DB.attribute_number_range[attribute]: + match DB.ATTRIBUTE_NUMBER_RANGE[attribute]: DB.NumberRange.UNIT: return _generate(NumberSliderScene, element, attribute) _: return _generate(NumberFieldScene, element, attribute) _: return _generate(UnrecognizedFieldScene, element, attribute) diff --git a/src/utils/FileUtils.gd b/src/utils/FileUtils.gd index cd92780..8d762a6 100644 --- a/src/utils/FileUtils.gd +++ b/src/utils/FileUtils.gd @@ -28,8 +28,8 @@ static func compare_svg_to_disk_contents(idx := -1) -> FileState: return FileState.DOES_NOT_EXIST # Check if importing the file's text into VectorTouch would change the current SVG text. # Avoid the parsing if checking the active tab. - var state_svg_text := State.svg_text if idx == -1 else SVGParser.root_to_editor_text(SVGParser.text_to_root(tab.get_true_svg_text()).svg) - if state_svg_text == SVGParser.root_to_editor_text(SVGParser.text_to_root(content).svg): + var state_svg_text := State.svg_text if idx == -1 else SVGParser.root_to_editor_markup(SVGParser.markup_to_root(tab.get_true_svg_text()).svg) + if state_svg_text == SVGParser.root_to_editor_markup(SVGParser.markup_to_root(content).svg): return FileState.SAME else: return FileState.DIFFERENT @@ -142,17 +142,14 @@ 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) -static func _finish_reference_load(data: Variant, file_path: String) -> void: +static func _finish_reference_load(data: Variant, file_path: String, final_callback: Callable) -> void: var img := Image.new() match file_path.get_extension().to_lower(): "svg": img.load_svg_from_string(data) "png": img.load_png_from_buffer(data) "jpg", "jpeg": img.load_jpg_from_buffer(data) "webp": img.load_webp_from_buffer(data) - load_reference_from_image(img) - -static func load_reference_from_image(img: Image) -> void: - Configs.savedata.get_active_tab().reference_image = ImageTexture.create_from_image(img) + final_callback.call(img) static func _is_native_preferred() -> bool: @@ -166,8 +163,8 @@ static func _choose_file_name() -> String: static func open_svg_import_dialog() -> void: _open_import_dialog(PackedStringArray(["svg"]), _apply_svg, true) -static func open_image_import_dialog() -> void: - _open_import_dialog(PackedStringArray(["png", "jpg", "jpeg", "webp", "svg"]), _finish_reference_load) +static func open_image_import_dialog(completion_callback: Callable) -> void: + _open_import_dialog(PackedStringArray(["png", "jpg", "jpeg", "webp", "svg"]), _finish_reference_load.bind(completion_callback)) static func open_xml_import_dialog(completion_callback: Callable) -> void: _open_import_dialog(PackedStringArray(["xml"]), completion_callback) @@ -259,10 +256,8 @@ static func _file_import_proceed(file_paths: PackedStringArray, completion_callb 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 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 := WebSafeFileAccess.open(file_path, FileAccess.READ) # If it's impossible to somehow import files from multiple dirs at the same time, @@ -290,12 +285,10 @@ static func _file_import_proceed(file_paths: PackedStringArray, completion_callb 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) + 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) + 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(): @@ -309,8 +302,7 @@ static func _file_import_proceed(file_paths: PackedStringArray, completion_callb 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, - false) + completion_callback.call(file.get_as_text(), file_path, proceed_callback, false) "xml": completion_callback.call(file.get_as_text()) _: completion_callback.call(file.get_buffer(file.get_length()), file_path) @@ -351,13 +343,11 @@ static func _apply_svg(data: Variant, file_path: String, proceed_callback := Cal Configs.savedata.remove_tab(tab_index) Configs.savedata.move_tab(Configs.savedata.get_tab_count() - 1, tab_index) 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.imported.connect(_on_import_panel_accepted_empty_tab_scenario.bind(data)) 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.imported.connect(_on_import_panel_accepted_transient_scenario.bind(file_path, data)) if not is_last_file: warning_panel.canceled.connect(proceed_callback) warning_panel.imported.connect(proceed_callback) diff --git a/src/utils/ImageExportData.gd b/src/utils/ImageExportData.gd index 24e8083..039e99f 100644 --- a/src/utils/ImageExportData.gd +++ b/src/utils/ImageExportData.gd @@ -57,6 +57,6 @@ func generate_image() -> Image: export_svg.set_attribute("width", export_svg.width * upscale_amount) export_svg.set_attribute("height", export_svg.height * upscale_amount) var img := Image.new() - img.load_svg_from_string(SVGParser.root_to_export_text(export_svg)) + img.load_svg_from_string(SVGParser.root_to_export_markup(export_svg)) img.fix_alpha_edges() # See godot issue 82579. return img diff --git a/src/utils/ThemeUtils.gd b/src/utils/ThemeUtils.gd index f82944a..b521ed0 100644 --- a/src/utils/ThemeUtils.gd +++ b/src/utils/ThemeUtils.gd @@ -18,6 +18,18 @@ static var black_or_white_counter_accent_color: Color static var warning_icon_color: Color static var info_icon_color: Color +static var hover_selected_inspector_frame_inner_color: Color +static var hover_selected_inspector_frame_title_color: Color +static var selected_inspector_frame_inner_color: Color +static var selected_inspector_frame_title_color: Color +static var active_inspector_frame_border_color: Color +static var hover_inspector_frame_inner_color: Color +static var hover_inspector_frame_title_color: Color +static var hover_inspector_frame_border_color: Color +static var inspector_frame_inner_color: Color +static var inspector_frame_title_color: Color +static var inspector_frame_border_color: Color + static var intermediate_color: Color # Color of button borders, derived from the accent color. static var soft_intermediate_color: Color # Color of button insides. static var softer_intermediate_color: Color # Color of tabs. @@ -112,6 +124,18 @@ static func recalculate_colors() -> void: gray_color = base_color.lerp(max_contrast_color, 0.5) black_or_white_counter_accent_color = extreme_theme_color if is_theme_dark else max_contrast_color + hover_selected_inspector_frame_inner_color = Color.from_hsv(0.625, 0.48, 0.27) if ThemeUtils.is_theme_dark else Color.from_hsv(0.625, 0.27, 0.925) + hover_selected_inspector_frame_title_color = hover_selected_inspector_frame_inner_color.lerp(max_contrast_color, 0.02) + selected_inspector_frame_inner_color = Color.from_hsv(0.625, 0.5, 0.25) if ThemeUtils.is_theme_dark else Color.from_hsv(0.625, 0.23, 0.925) + selected_inspector_frame_title_color = selected_inspector_frame_inner_color.lerp(max_contrast_color, 0.02) + active_inspector_frame_border_color = Color.from_hsv(0.6, 0.75, 0.8) if ThemeUtils.is_theme_dark else Color.from_hsv(0.6, 0.75, 0.625) + hover_inspector_frame_inner_color = Color.from_hsv(0.625, 0.57, 0.19) if ThemeUtils.is_theme_dark else Color.from_hsv(0.625, 0.16, 0.9) + hover_inspector_frame_title_color = hover_inspector_frame_inner_color.lerp(max_contrast_color, 0.02) + hover_inspector_frame_border_color = Color.from_hsv(0.6, 0.55, 0.45) if ThemeUtils.is_theme_dark else Color.from_hsv(0.6, 0.55, 0.8) + inspector_frame_inner_color = Color.from_hsv(0.625, 0.6, 0.16) if ThemeUtils.is_theme_dark else Color.from_hsv(0.625, 0.12, 0.9) + inspector_frame_title_color = inspector_frame_inner_color.lerp(max_contrast_color, 0.02) + inspector_frame_border_color = Color.from_hsv(0.6, 0.5, 0.35) if ThemeUtils.is_theme_dark else Color.from_hsv(0.6, 0.5, 0.875) + # Icons warning_icon_color = Color("#FFD54F") info_icon_color = Color("#64B5F6") diff --git a/src/utils/TranslationUtils.gd b/src/utils/TranslationUtils.gd index f13fbf9..c1ca1e5 100644 --- a/src/utils/TranslationUtils.gd +++ b/src/utils/TranslationUtils.gd @@ -1,21 +1,27 @@ +## A utility class for handling localization. @abstract class_name TranslationUtils +# Returns a human-readable name for the given locale. +# Handles special cases for certain locales, falls back to TranslationServer for others. static func _get_locale_name(locale: String) -> String: match locale: "pt_BR": return "Brazilian Portuguese" "zh_CN": return "Simplified Chinese" return TranslationServer.get_locale_name(locale) +## Converts a locale string to a language-REGION format. static func get_locale_string(locale: String) -> String: if not "_" in locale: return locale.to_upper() var separator_pos := locale.find("_") return locale.left(separator_pos) + "-" + locale.right(-separator_pos - 1).to_upper() +## Returns a display-friendly locale string with both name and code, such as "Brazilian Portuguese (pt-BR)". static func get_locale_display(locale: String) -> String: return "%s (%s)" % [_get_locale_name(locale), get_locale_string(locale)] +## Returns the translated description for a given action. If for_button is true, uses a shorter description. static func get_action_description(action_name: String, for_button := false) -> String: match action_name: "export": return Translator.translate("Export") @@ -88,7 +94,8 @@ static func get_action_description(action_name: String, for_button := false) -> "toggle_fullscreen": return Translator.translate("Toggle fullscreen") _: return action_name - +## Returns a translated description for an SVG path command character. +## If omit_relativity is true, doesn't append "(Relative)"/"(Absolute)" suffix. static func get_path_command_description(command_char: String, omit_relativity := false) -> String: var description: String match command_char: @@ -111,7 +118,7 @@ static func get_path_command_description(command_char: String, omit_relativity : else: return description + " (" + Translator.translate("Absolute") + ")" - +## Returns the translated name for a layout part. static func get_layout_part_name(layout_part: Utils.LayoutPart) -> String: match layout_part: Utils.LayoutPart.CODE_EDITOR: return Translator.translate("Code editor") @@ -119,28 +126,31 @@ static func get_layout_part_name(layout_part: Utils.LayoutPart) -> String: Utils.LayoutPart.VIEWPORT: return Translator.translate("Viewport") _: return "" - +## Generates an alert text for unsupported file extensions. 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]) + allowed_extensions[i] = _get_extension_readable_name(allowed_extensions[i]) var extension_list := ", ".join(allowed_extensions) return Translator.translate("Only {extension_list} files are supported for this operation.").format({"extension_list": extension_list}) +## Generates title text for file selection dialogs. 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")}) + return Translator.translate("Select {format} files").format({"format": _get_extension_readable_name("svg")}) else: 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])}) + return Translator.translate("Select an {format} file").format({"format": _get_extension_readable_name(extensions[0])}) +## Generates title text for file save dialogs. static func get_file_dialog_save_mode_title_text(extension: String) -> String: - return Translator.translate("Save the {format} file").format({"format": get_extension_readable_name(extension)}) + return Translator.translate("Save the {format} file").format({"format": _get_extension_readable_name(extension)}) -static func get_extension_readable_name(extension: String) -> String: +# Converts a file extension to a human-readable format name. +static func _get_extension_readable_name(extension: String) -> String: match extension: "svg": return "SVG" "png": return "PNG" diff --git a/src/utils/Translator.gd b/src/utils/Translator.gd index 743e5f7..2197fd5 100644 --- a/src/utils/Translator.gd +++ b/src/utils/Translator.gd @@ -1,5 +1,5 @@ -# A wrapper around basic TranslationServer methods. There is an update_translations script -# which checks all Translator methods used across the codebase to collect their strings. +## A wrapper around basic TranslationServer methods. +## The update_translations.gd script collects strings from all Translator methods used across the codebase. @abstract class_name Translator static func translate(string: String) -> String: diff --git a/src/utils/Utils.gd b/src/utils/Utils.gd index 72dd48f..e37a039 100644 --- a/src/utils/Utils.gd +++ b/src/utils/Utils.gd @@ -32,12 +32,7 @@ static func get_file_name(string: String) -> String: # 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") - + var home_dir := OS.get_environment("USERPROFILE" if OS.get_name() == "Windows" else "HOME") if file_path.begins_with(home_dir): return "~/" + file_path.trim_prefix(home_dir).trim_prefix("/").trim_prefix("\\") return file_path @@ -64,8 +59,7 @@ static func get_cubic_bezier_points(cp1: Vector2, cp2: Vector2, cp3: Vector2, cp return curve.tessellate(6, 1) static func get_quadratic_bezier_points(cp1: Vector2, cp2: Vector2, cp3: Vector2) -> PackedVector2Array: - return Utils.get_cubic_bezier_points( - cp1, 2/3.0 * (cp2 - cp1), 2/3.0 * (cp2 - cp3), cp3) + return Utils.get_cubic_bezier_points(cp1, 2/3.0 * (cp2 - cp1), 2/3.0 * (cp2 - cp3), cp3) # Calculate quadratic bezier point coordinate along an axis. static func quadratic_bezier_point(p0: float, p1: float, p2: float, t: float) -> float: diff --git a/src/utils/Utils64Bit.gd b/src/utils/Utils64Bit.gd index da03260..c1b19f9 100644 --- a/src/utils/Utils64Bit.gd +++ b/src/utils/Utils64Bit.gd @@ -1,15 +1,16 @@ -# Vector2 and Transform2D aren't precise enough to have their numbers used directly -# in the SVG, as they are 32-bit. VectorTouch uses PackedFloat64Array to mock them -# and this class implements the necessary functionality to make them work. +## Vector2 and Transform2D aren't precise enough to have their numbers used directly in the SVG, as they are 32-bit. +## VectorTouch uses PackedFloat64Array to mock them and this class implements the necessary functionality to make them work. @abstract class_name Utils64Bit +## Converts a PackedFloat64Array with two elements meant to represent a Vector2 into a Vector2. static func get_vector(vector: PackedFloat64Array) -> Vector2: return Vector2(vector[0], vector[1]) +## Converts a PackedFloat64Array with six elements meant to represent a Transform2D into a Transform2D. static func get_transform(transform: PackedFloat64Array) -> Transform2D: return Transform2D(Vector2(transform[0], transform[1]), Vector2(transform[2], transform[3]), Vector2(transform[4], transform[5])) -# Vector2 * Transform2D +## 64-bit version of Vector2 * Transform2D = Vector2 static func transform_vector_mult(transform: PackedFloat64Array, vector: PackedFloat64Array) -> PackedFloat64Array: var x := vector[0] var y := vector[1] @@ -21,7 +22,7 @@ static func transform_vector_mult(transform: PackedFloat64Array, vector: PackedF var oy := transform[5] return PackedFloat64Array([xx * x + yx * y + ox, xy * x + yy * y + oy]) -# Transform2D.determinant() +## 64-bit version of Transform2D.determinant() = float static func get_transform_determinant(transform: PackedFloat64Array) -> float: var xx := transform[0] var xy := transform[1] @@ -29,7 +30,7 @@ static func get_transform_determinant(transform: PackedFloat64Array) -> float: var yy := transform[3] return xx * yy - xy * yx -# Transform2D.affine_inverse() +## 64-bit version of Transform2D.affine_inverse() = Transform2D static func get_transform_affine_inverse(transform: PackedFloat64Array) -> PackedFloat64Array: var det := get_transform_determinant(transform) if det == 0: @@ -51,7 +52,7 @@ static func get_transform_affine_inverse(transform: PackedFloat64Array) -> Packe var new_oy := -(new_xy * ox + new_yy * oy) return PackedFloat64Array([new_xx, new_xy, new_yx, new_yy, new_ox, new_oy]) -# Transform2D * Transform2D +## 64-bit version of Transform2D * Transform2D = Transform2D static func transforms_mult(a: PackedFloat64Array, b: PackedFloat64Array) -> PackedFloat64Array: var a_xx := a[0] var a_xy := a[1] @@ -75,11 +76,11 @@ static func transforms_mult(a: PackedFloat64Array, b: PackedFloat64Array) -> Pac var oy := a_xy * b_ox + a_yy * b_oy + a_oy return PackedFloat64Array([xx, xy, yx, yy, ox, oy]) -# Vector2.project(Vector2) +## 64-bit version of Vector2.project(Vector2) = Vector2 static func vector_project(a: PackedFloat64Array, b: PackedFloat64Array) -> PackedFloat64Array: var scalar := dot(a, b) / dot(b, b) return [b[0] * scalar, b[1] * scalar] -# Vector2.dot(Vector2) +## 64-bit version of Vector2.dot(Vector2) = float static func dot(a: PackedFloat64Array, b: PackedFloat64Array) -> float: return a[0] * b[0] + a[1] * b[1] diff --git a/src/utils/XIDUtils.gd b/src/utils/XIDUtils.gd index 8db32bd..afb136e 100644 --- a/src/utils/XIDUtils.gd +++ b/src/utils/XIDUtils.gd @@ -1,3 +1,6 @@ +## XID stands for XML Node ID. These IDs take the form of a PackedInt32Array with indices. +## For example, if an XML node is the third child of the first element, its XID is [0, 2]. +## This class provides utilities for working with XIDs. @abstract class_name XIDUtils # [1] > [1, 2] > [1, 0] > [0] @@ -12,37 +15,41 @@ static func compare(xid1: PackedInt32Array, xid2: PackedInt32Array) -> bool: static func compare_reverse(xid1: PackedInt32Array, xid2: PackedInt32Array) -> bool: return compare(xid2, xid1) -# Indirect parent, i.e. ancestor. Passing the root element as parent will return false. -static func is_parent(parent: PackedInt32Array, child: PackedInt32Array) -> bool: - if parent.is_empty(): +## Checks if a XID describes an element that's an ancestor to another's XID. +## Passing the root element as ancestor returns false. +static func is_ancestor(ancestor: PackedInt32Array, child: PackedInt32Array) -> bool: + if ancestor.is_empty(): return false - var parent_size := parent.size() - if parent_size >= child.size(): + var ancestor_size := ancestor.size() + if ancestor_size >= child.size(): return false - for i in parent_size: - if parent[i] != child[i]: + for i in ancestor_size: + if ancestor[i] != child[i]: return false return true -static func is_parent_or_self(parent: PackedInt32Array, child: PackedInt32Array) -> bool: - if parent.is_empty(): +## Checks if a XID describes an element that's an ancestor to another's XID. +## Also returns true if the two elements are the same. Passing the root element as ancestor returns false. +static func is_ancestor_or_self(ancestor: PackedInt32Array, child: PackedInt32Array) -> bool: + if ancestor.is_empty(): return false - var parent_size := parent.size() - if parent_size > child.size(): + var ancestor_size := ancestor.size() + if ancestor_size > child.size(): return false - for i in parent_size: - if parent[i] != child[i]: + for i in ancestor_size: + if ancestor[i] != child[i]: return false return true - +## Returns the XID representing the parent of the node represented by the passed XID. static func get_parent_xid(xid: PackedInt32Array) -> PackedInt32Array: var parent_xid := xid.duplicate() parent_xid.resize(xid.size() - 1) return parent_xid +## Returns true if the two XIDs represent sibling nodes or the same node. static func are_siblings_or_same(xid1: PackedInt32Array, xid2: PackedInt32Array) -> bool: if xid1.size() != xid2.size(): return false @@ -51,7 +58,7 @@ static func are_siblings_or_same(xid1: PackedInt32Array, xid2: PackedInt32Array) return false return true -# Filter out all descendants. +## Filters out from the passed array of XIDs all of the ones that are descendants of another XID. static func filter_descendants(xids: Array[PackedInt32Array]) -> Array[PackedInt32Array]: var new_xids: Array[PackedInt32Array] = xids.duplicate() new_xids.sort_custom(compare_reverse) @@ -60,7 +67,7 @@ static func filter_descendants(xids: Array[PackedInt32Array]) -> Array[PackedInt var i := 1 while i < new_xids.size(): var xid := new_xids[i] - if is_parent_or_self(last_accepted, xid): + if is_ancestor_or_self(last_accepted, xid): new_xids.remove_at(i) else: last_accepted = new_xids[i] @@ -68,6 +75,7 @@ static func filter_descendants(xids: Array[PackedInt32Array]) -> Array[PackedInt return new_xids # Not typed to Array[PackedInt32Array] because typed arrays were annoying. +## Returns true if the two passed arrays of XIDs contain the same XIDs. static func are_xid_lists_same(xid_list_1: Array, xid_list_2: Array) -> bool: if xid_list_1.size() != xid_list_2.size(): return false diff --git a/translations/GodSVG.pot b/translations/GodSVG.pot index dac9eca..eeccf1d 100644 --- a/translations/GodSVG.pot +++ b/translations/GodSVG.pot @@ -41,24 +41,24 @@ msgid "OK" msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to proceed?" +msgid "The graphic can be exported only as SVG because its proportions are too extreme." msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "Export SVG" +msgid "The graphic can be exported only as SVG because its size is not defined." msgstr "" -#: src/autoload/HandlerGUI.gd: src/ui_parts/export_menu.gd: -#: src/utils/TranslationUtils.gd: -msgid "Export" +#: src/autoload/HandlerGUI.gd: +msgid "Do you want to proceed?" msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgid "Export SVG" msgstr "" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." +#: src/autoload/HandlerGUI.gd: src/ui_parts/export_menu.gd: +#: src/utils/TranslationUtils.gd: +msgid "Export" msgstr "" #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: @@ -278,6 +278,10 @@ msgstr "" msgid "Dimensions" msgstr "" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "" @@ -314,10 +318,6 @@ msgstr "" msgid "Copy" msgstr "" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -391,6 +391,10 @@ msgstr "" msgid "New shape" msgstr "" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "" @@ -407,10 +411,6 @@ msgstr "" msgid "Unrecognized attribute" msgstr "" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "" @@ -578,6 +578,10 @@ msgstr "" msgid "Eyedropper" msgstr "" +#: src/ui_widgets/palette_config.gd: +msgid "Add new color" +msgstr "" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "" @@ -1378,6 +1382,10 @@ msgstr "" msgid "Viewport" msgstr "" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "" + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "" @@ -1393,7 +1401,3 @@ msgstr "" #: src/utils/TranslationUtils.gd: msgid "Save the {format} file" msgstr "" - -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "" diff --git a/translations/bg.po b/translations/bg.po index 50d4460..efe77c0 100644 --- a/translations/bg.po +++ b/translations/bg.po @@ -41,6 +41,14 @@ msgstr "Това ще се свърже с github.com за да сравни ч msgid "OK" msgstr "Добре" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Графиката може да бъде експортирана само като SVG защото височината и широчината са твърде различни." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Графиката може да бъде експортирана само като SVG защото размерът не е определен." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Искаш ли да продължиш?" @@ -54,14 +62,6 @@ msgstr "Експортирай SVG-то" msgid "Export" msgstr "Експортирай" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Графиката може да бъде експортирана само като SVG защото височината и широчината са твърде различни." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Графиката може да бъде експортирана само като SVG защото размерът не е определен." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -279,6 +279,10 @@ msgstr "Добави елемент" msgid "Dimensions" msgstr "Измерения" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Размерът на визуализацията е ограничен до {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Размер" @@ -315,10 +319,6 @@ msgstr "Височина" msgid "Copy" msgstr "Копирай" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "Размерът на визуализацията е ограничен до {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -392,6 +392,10 @@ msgstr "Файл с името \"{file_name}\" вече съществува. З msgid "New shape" msgstr "Нова фигура" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Синтактична грешка" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Проблеми в импортирането" @@ -408,10 +412,6 @@ msgstr "Непознат елемент" msgid "Unrecognized attribute" msgstr "Непознат атрибут" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Синтактична грешка" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Добави елемент" @@ -579,6 +579,10 @@ msgstr "Ключови цветове" msgid "Eyedropper" msgstr "Пипета" +#: src/ui_widgets/palette_config.gd: +msgid "Add new color" +msgstr "Добави нов цвят" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Неименуваните палети няма да бъдат показани." @@ -1379,6 +1383,10 @@ msgstr "Инспектор" msgid "Viewport" msgstr "Гледка" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Единствено {extension_list} файлове се поддържат от тази операция." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Избери {format} файлове" @@ -1394,7 +1402,3 @@ msgstr "Избери {format} файл" #: src/utils/TranslationUtils.gd: msgid "Save the {format} file" msgstr "Запази {format} файла" - -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Единствено {extension_list} файлове се поддържат от тази операция." diff --git a/translations/de.po b/translations/de.po index 5a12abd..d9135bf 100644 --- a/translations/de.po +++ b/translations/de.po @@ -41,6 +41,14 @@ msgstr "Dadurch wird eine Verbindung zu github.com hergestellt, um Versionsnumme msgid "OK" msgstr "OK" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Die Grafik kann nur als SVG exportiert werden, da ihre Proportionen zu extrem sind." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Die Grafik kann nur als SVG exportiert werden, da ihre Größe nicht definiert ist." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Möchten Sie fortfahren?" @@ -54,14 +62,6 @@ msgstr "SVG exportieren" msgid "Export" msgstr "Exportieren" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Die Grafik kann nur als SVG exportiert werden, da ihre Proportionen zu extrem sind." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Die Grafik kann nur als SVG exportiert werden, da ihre Größe nicht definiert ist." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -280,6 +280,10 @@ msgstr "Neues Element" msgid "Dimensions" msgstr "Dimensionen" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Vorschaubildgröße ist auf {dimensions} limitiert." + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Größe" @@ -316,10 +320,6 @@ msgstr "Höhe" 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." - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -393,6 +393,10 @@ msgstr "Eine Datei mit dem Namen \"{file_name}\" existiert bereits. Ersetzen wir msgid "New shape" msgstr "Neue Form" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Syntaxfehler" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Probleme beim Importieren" @@ -409,10 +413,6 @@ msgstr "Unerkanntes Element" msgid "Unrecognized attribute" msgstr "Unerkanntes Attribut" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Syntaxfehler" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Element hinzufügen" @@ -581,6 +581,11 @@ msgstr "Farbschlüsselwörter" msgid "Eyedropper" msgstr "Pipette" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Farbe hinzufügen" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Unbenannte Paletten werden nicht angezeigt." @@ -1394,6 +1399,10 @@ msgstr "Inspektor" msgid "Viewport" msgstr "Ansichtsfenster" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Nur {extension_list}-Dateien werden für diese Operation unterstützt." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "{format}-Dateien auswählen" @@ -1410,10 +1419,6 @@ msgstr "{format}-Datei auswählen" msgid "Save the {format} file" msgstr "Die {format}-Datei speichern" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Nur {extension_list}-Dateien werden für diese Operation unterstützt." - #~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." #~ msgstr "Wenn diese Option deaktiviert ist, bleibt der Fenstertitel \"VectorTouch\", ohne die aktuelle Datei anzuzeigen." @@ -1573,9 +1578,6 @@ msgstr "Nur {extension_list}-Dateien werden für diese Operation unterstützt." #~ msgid "Couple/Decouple" #~ msgstr "Koppeln/Entkoppeln" -#~ msgid "Add color" -#~ msgstr "Farbe hinzufügen" - #~ msgid "Remove plus sign" #~ msgstr "Pluszeichen entfernen" diff --git a/translations/en.po b/translations/en.po index c78f058..890f615 100644 --- a/translations/en.po +++ b/translations/en.po @@ -42,24 +42,24 @@ msgid "OK" msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to proceed?" +msgid "The graphic can be exported only as SVG because its proportions are too extreme." msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "Export SVG" +msgid "The graphic can be exported only as SVG because its size is not defined." msgstr "" -#: src/autoload/HandlerGUI.gd: src/ui_parts/export_menu.gd: -#: src/utils/TranslationUtils.gd: -msgid "Export" +#: src/autoload/HandlerGUI.gd: +msgid "Do you want to proceed?" msgstr "" #: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgid "Export SVG" msgstr "" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." +#: src/autoload/HandlerGUI.gd: src/ui_parts/export_menu.gd: +#: src/utils/TranslationUtils.gd: +msgid "Export" msgstr "" #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: @@ -279,6 +279,10 @@ msgstr "" msgid "Dimensions" msgstr "" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "" @@ -315,10 +319,6 @@ msgstr "" msgid "Copy" msgstr "" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -392,6 +392,10 @@ msgstr "" msgid "New shape" msgstr "" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "" @@ -408,10 +412,6 @@ msgstr "" msgid "Unrecognized attribute" msgstr "" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "" @@ -579,6 +579,10 @@ msgstr "" msgid "Eyedropper" msgstr "" +#: src/ui_widgets/palette_config.gd: +msgid "Add new color" +msgstr "" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "" @@ -1379,6 +1383,10 @@ msgstr "" msgid "Viewport" msgstr "" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "" + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "" @@ -1394,7 +1402,3 @@ msgstr "" #: src/utils/TranslationUtils.gd: msgid "Save the {format} file" msgstr "" - -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "" diff --git a/translations/es.po b/translations/es.po index 32d7e00..da7880c 100644 --- a/translations/es.po +++ b/translations/es.po @@ -41,6 +41,14 @@ msgstr "Esto se conectará a github.com para comparar los números de versión. msgid "OK" msgstr "OK" +#: 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/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "¿Quieres continuar?" @@ -54,14 +62,6 @@ msgstr "Exportar SVG" 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!" @@ -280,6 +280,10 @@ msgstr "Nuevo elemento" msgid "Dimensions" msgstr "Dimensiones" +#: 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/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Tamaño" @@ -316,10 +320,6 @@ msgstr "Alto" 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/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -393,6 +393,10 @@ msgstr "Ya existe un archivo llamado \"{file_name}\". ¡Reemplazarlo sobrescribi msgid "New shape" msgstr "Nueva forma" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Error de sintaxis" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Problemas de la importación" @@ -409,10 +413,6 @@ msgstr "Elemento no reconocido" 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" @@ -581,6 +581,11 @@ msgstr "Palabras clave de colores" msgid "Eyedropper" msgstr "Cuentagotas" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Color del texto" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "No se mostrarán las paletas sin nombre." @@ -1394,6 +1399,10 @@ msgstr "Inspector" msgid "Viewport" msgstr "Ventana gráfica" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Solo se admiten archivos {extension_list} para esta operación." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Selecciona archivos {format}" @@ -1410,10 +1419,6 @@ msgstr "Selecciona un archivo {format}" msgid "Save the {format} file" msgstr "Guardar el archivo {format}" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Solo se admiten archivos {extension_list} para esta operación." - #~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." #~ msgstr "Si está desactivado, el título de la ventana permanece como \"VectorTouch\" sin incluir el archivo actual." diff --git a/translations/et.po b/translations/et.po index ac5f7b0..7398512 100644 --- a/translations/et.po +++ b/translations/et.po @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: VectorTouch\n" +"Project-Id-Version: GodSVG\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: \n" @@ -18,12 +18,12 @@ msgid "translation-credits" msgstr "Kiisu-Master" #: src/autoload/HandlerGUI.gd: -msgid "Quit VectorTouch" -msgstr "Sulge VectorTouch" +msgid "Quit GodSVG" +msgstr "Sulge GodSVG" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to quit VectorTouch?" -msgstr "Kas te tahate sulgeda VectorTouch?" +msgid "Do you want to quit GodSVG?" +msgstr "Kas te tahate sulgeda GodSVG?" #: src/autoload/HandlerGUI.gd: msgid "Quit" @@ -41,6 +41,14 @@ msgstr "See ühendab github.com-i, et saada versioonide teabe. Muid andmeid ei k msgid "OK" msgstr "Okei" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Graafika saab eksportida ainult SVG-na, sest selle proportsioonid on liiga ekstreemsed." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Graafika saab eksportida ainult SVG-na, sest selle suurus pole defineeritud." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Kas te tahate jätkata?" @@ -54,14 +62,6 @@ msgstr "Ekspordi SVG" msgid "Export" msgstr "Ekspordi" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Graafika saab eksportida ainult SVG-na, sest selle proportsioonid on liiga ekstreemsed." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -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/utils/FileUtils.gd: msgid "Alert!" @@ -280,6 +280,10 @@ msgstr "Uus element" msgid "Dimensions" msgstr "Dimensioonid" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Eelvaade on piiratud suurusele {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Suurus" @@ -316,10 +320,6 @@ msgstr "Kõrgus" msgid "Copy" msgstr "Kopeeri" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "Eelvaade on piiratud suurusele {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -393,6 +393,10 @@ msgstr "Fail nimega \"{file_name}\" on juba olemas, asendamine kirjutab selle si msgid "New shape" msgstr "Uus kujund" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Süntaksiviga" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Impordi probleemid" @@ -409,10 +413,6 @@ msgstr "Tundmatu element" msgid "Unrecognized attribute" msgstr "Tundmatu atribuut" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Süntaksiviga" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Lisa element" @@ -532,8 +532,8 @@ msgid "View all releases" msgstr "Näita kõiki väljaandeid" #: src/ui_parts/update_menu.gd: -msgid "VectorTouch is up-to-date." -msgstr "VectorTouch on ajakohane." +msgid "GodSVG is up-to-date." +msgstr "GodSVG on ajakohane." #: src/ui_parts/update_menu.gd: msgid "New versions available!" @@ -581,6 +581,11 @@ msgstr "Värvi võtmesõnad" msgid "Eyedropper" msgstr "Värvipipett" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Teksti värv" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Nimetuid värvipalette ei kuvata." @@ -662,7 +667,7 @@ msgstr "Lisa kiirklahv" msgid "Press keys…" msgstr "Vajuta klahve…" -#. Refers to the formatter used for VectorTouch's code editor. +#. Refers to the formatter used for GodSVG's code editor. #: src/ui_widgets/settings_content_generic.gd: msgid "Editor formatter" msgstr "Redaktori vormindus" @@ -980,7 +985,7 @@ msgid "Sync window title to file name" msgstr "Sünkrooni akna tiitel avatud faili nimega" #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." +msgid "When enabled, adds the current file name before the \"GodSVG\" window title." msgstr "" #: src/ui_widgets/settings_content_generic.gd: @@ -997,8 +1002,8 @@ msgstr "" #: src/ui_widgets/settings_content_generic.gd: #, fuzzy -msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." -msgstr "Sisselülitamisel kasutab süsteemi vaikefailidialoogi. Väljalülitamisel VectorTouch sisseehitatud failidialoogi." +msgid "When enabled, uses your operating system's native file dialog instead of GodSVG's built-in one." +msgstr "Sisselülitamisel kasutab süsteemi vaikefailidialoogi. Väljalülitamisel GodSVG sisseehitatud failidialoogi." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting has no effect in the current configuration." @@ -1045,8 +1050,8 @@ msgid "New transform" msgstr "Uus teisendus" #: src/ui_widgets/unrecognized_field.gd: -msgid "VectorTouch doesn’t recognize this attribute" -msgstr "VectorTouch ei tunne seda atribuuti" +msgid "GodSVG doesn’t recognize this attribute" +msgstr "GodSVG ei tunne seda atribuuti" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" @@ -1101,8 +1106,8 @@ msgid "Don't save" msgstr "Ära salvesta" #: src/utils/FileUtils.gd: -msgid "{file_path} is already being edited inside VectorTouch." -msgstr "{file_path} on juba avatud VectorTouchs." +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}." @@ -1309,20 +1314,20 @@ msgid "Open Donate menu" msgstr "Ava annetuste menüü" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch repository" -msgstr "VectorTouch repositoorium" +msgid "GodSVG repository" +msgstr "GodSVG repositoorium" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch repository" -msgstr "Ava VectorTouch repositoorium" +msgid "Open GodSVG repository" +msgstr "Ava GodSVG repositoorium" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch website" -msgstr "VectorTouch veebileht" +msgid "GodSVG website" +msgstr "GodSVG veebileht" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch website" -msgstr "Ava VectorTouch veebileht" +msgid "Open GodSVG website" +msgstr "Ava GodSVG veebileht" #: src/utils/TranslationUtils.gd: msgid "Check for updates" @@ -1393,6 +1398,10 @@ msgstr "Inspektor" msgid "Viewport" msgstr "Vaade" +#: src/utils/TranslationUtils.gd: +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 "Select {format} files" msgstr "Vali {format} faile" @@ -1409,12 +1418,8 @@ msgstr "Vali {format} fail" msgid "Save the {format} file" msgstr "Salvesta {format} fail" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Ainult {extension_list} failid on toetatud selle tehingu jaoks." - -#~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." -#~ msgstr "Väljalülitamisel jääb akna nimieks \"VectorTouch\" sõltumata avatud failist." +#~ msgid "If turned off, the window title remains as \"GodSVG\" without including the current file." +#~ msgstr "Väljalülitamisel jääb akna nimieks \"GodSVG\" sõltumata avatud failist." #~ msgid "Keep unrecognized XML structures" #~ msgstr "Hoia tundmatud XML struktuurid alles" diff --git a/translations/fr.po b/translations/fr.po index 0014e1f..6639cd3 100644 --- a/translations/fr.po +++ b/translations/fr.po @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: VectorTouch\n" +"Project-Id-Version: GodSVG\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: \n" @@ -18,12 +18,12 @@ msgid "translation-credits" msgstr "thatoddshade " #: src/autoload/HandlerGUI.gd: -msgid "Quit VectorTouch" -msgstr "Quitter VectorTouch" +msgid "Quit GodSVG" +msgstr "Quitter GodSVG" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to quit VectorTouch?" -msgstr "Voulez-vous quitter VectorTouch ?" +msgid "Do you want to quit GodSVG?" +msgstr "Voulez-vous quitter GodSVG ?" #: src/autoload/HandlerGUI.gd: msgid "Quit" @@ -41,6 +41,14 @@ msgstr "Une connexion à github.com sera effectuée pour comparer les numéros d msgid "OK" msgstr "OK" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Le graphique ne peut être exporté qu'en SVG car ses proportions sont trop extrêmes." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Le graphique ne peut être exporté qu'en SVG car sa taille n'a pas été pas définie." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Voulez-vous poursuivre ?" @@ -54,14 +62,6 @@ msgstr "Exporter le SVG" msgid "Export" msgstr "Exporter" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Le graphique ne peut être exporté qu'en SVG car ses proportions sont trop extrêmes." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Le graphique ne peut être exporté qu'en SVG car sa taille n'a pas été pas définie." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -280,6 +280,10 @@ msgstr "Nouvel élément" msgid "Dimensions" msgstr "Dimensions" +#: 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}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Taille" @@ -316,10 +320,6 @@ msgstr "Hauteur" 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}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -393,6 +393,10 @@ msgstr "Un fichier nommé « {file_name} » existe déjà. Le remplacer écras msgid "New shape" msgstr "Nouvelle forme" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Erreur de syntaxe" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Importer des problèmes" @@ -409,10 +413,6 @@ msgstr "Élément non reconnu" msgid "Unrecognized attribute" msgstr "Attribut non reconnu" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Erreur de syntaxe" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Ajouter un élément" @@ -532,8 +532,8 @@ msgid "View all releases" msgstr "Voir toutes les versions" #: src/ui_parts/update_menu.gd: -msgid "VectorTouch is up-to-date." -msgstr "VectorTouch est à jour." +msgid "GodSVG is up-to-date." +msgstr "GodSVG est à jour." #: src/ui_parts/update_menu.gd: msgid "New versions available!" @@ -581,6 +581,11 @@ msgstr "Colorier les mots-clés" msgid "Eyedropper" msgstr "Pipette" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Couleur de texte" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Les palettes sans nom ne seront pas montrées." @@ -662,7 +667,7 @@ msgstr "Ajouter un raccourci" msgid "Press keys…" msgstr "Appuyez sur des touches…" -#. Refers to the formatter used for VectorTouch's code editor. +#. Refers to the formatter used for GodSVG's code editor. #: src/ui_widgets/settings_content_generic.gd: msgid "Editor formatter" msgstr "Formatage de l'éditeur" @@ -982,7 +987,7 @@ msgid "Sync window title to file name" msgstr "Synchroniser le titre de la fenêtre avec le nom du fichier" #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." +msgid "When enabled, adds the current file name before the \"GodSVG\" window title." msgstr "" #: src/ui_widgets/settings_content_generic.gd: @@ -999,8 +1004,8 @@ msgstr "" #: src/ui_widgets/settings_content_generic.gd: #, fuzzy -msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." -msgstr "Si activé, utilise le sélecteur de fichiers système. Si désactivé, utilise la boîte de dialogue intégrée de VectorTouch." +msgid "When enabled, uses your operating system's native file dialog instead of GodSVG's built-in one." +msgstr "Si activé, utilise le sélecteur de fichiers système. Si désactivé, utilise la boîte de dialogue intégrée de GodSVG." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting has no effect in the current configuration." @@ -1047,8 +1052,8 @@ msgid "New transform" msgstr "Nouvelle transformation" #: src/ui_widgets/unrecognized_field.gd: -msgid "VectorTouch doesn’t recognize this attribute" -msgstr "VectorTouch ne reconnaît pas cet attribut" +msgid "GodSVG doesn’t recognize this attribute" +msgstr "GodSVG ne reconnaît pas cet attribut" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" @@ -1103,8 +1108,8 @@ msgid "Don't save" msgstr "Ne pas enregistrer" #: src/utils/FileUtils.gd: -msgid "{file_path} is already being edited inside VectorTouch." -msgstr "{file_path} est déjà en cours de modification dans VectorTouch." +msgid "{file_path} is already being edited inside GodSVG." +msgstr "{file_path} est déjà en cours de modification dans GodSVG." #: src/utils/FileUtils.gd: msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1311,20 +1316,20 @@ msgid "Open Donate menu" msgstr "Ouvrir le menu « Faire un don »" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch repository" -msgstr "Dépôt de VectorTouch" +msgid "GodSVG repository" +msgstr "Dépôt de GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch repository" -msgstr "Ouvrir le dépôt de VectorTouch" +msgid "Open GodSVG repository" +msgstr "Ouvrir le dépôt de GodSVG" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch website" -msgstr "Site web de VectorTouch" +msgid "GodSVG website" +msgstr "Site web de GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch website" -msgstr "Ouvrir le site web de VectorTouch" +msgid "Open GodSVG website" +msgstr "Ouvrir le site web de GodSVG" #: src/utils/TranslationUtils.gd: msgid "Check for updates" @@ -1395,6 +1400,10 @@ msgstr "Inspecteur" msgid "Viewport" msgstr "Zone d'affichage" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Seuls les fichiers {extension_list} sont supportés par cette opération." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Sélectionner des fichiers {format}" @@ -1411,12 +1420,8 @@ msgstr "Sélectionner un fichier {format}" msgid "Save the {format} file" msgstr "Enregistrer le fichier {format}" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Seuls les fichiers {extension_list} sont supportés par cette opération." - -#~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." -#~ msgstr "Si désactivé, le titre de la fenêtre restera simplement « VectorTouch » quel que soit le fichier actuel." +#~ msgid "If turned off, the window title remains as \"GodSVG\" without including the current file." +#~ msgstr "Si désactivé, le titre de la fenêtre restera simplement « GodSVG » quel que soit le fichier actuel." #~ msgid "Keep unrecognized XML structures" #~ msgstr "Conserver les structures XML non reconnues" @@ -1482,8 +1487,8 @@ msgstr "Seuls les fichiers {extension_list} sont supportés par cette opération #~ msgid "Wraps the mouse cursor around when panning the viewport." #~ msgstr "Déplace le pointeur de souris d'un bout à l'autre de l'écran lorsque le bord est atteint lors d'un balayage." -#~ msgid "This requires VectorTouch to connect to the internet." -#~ msgstr "Ceci nécessite que VectorTouch se connecte à Internet." +#~ msgid "This requires GodSVG to connect to the internet." +#~ msgstr "Ceci nécessite que GodSVG se connecte à Internet." #~ msgid "Open file" #~ msgstr "Ouvrir le fichier" diff --git a/translations/nl.po b/translations/nl.po index 46d5948..ba005dd 100644 --- a/translations/nl.po +++ b/translations/nl.po @@ -41,6 +41,14 @@ msgstr "Dit zal een verbinding maken naar github.com om de versienummers te verg msgid "OK" msgstr "OK" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "De afbeelding kan alleen maar als SVG geëxporteerd worden door zijn extreme verhoudingen." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "De afbeelding kan alleen maar als SVG geëxporteerd worden want zijn maat is niet gedefiniëerd." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Wil je voortgaan?" @@ -54,14 +62,6 @@ msgstr "Exporteer SVG" msgid "Export" msgstr "Exporteren" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "De afbeelding kan alleen maar als SVG geëxporteerd worden door zijn extreme verhoudingen." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "De afbeelding kan alleen maar als SVG geëxporteerd worden want zijn maat is niet gedefiniëerd." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -76,9 +76,8 @@ msgid "Restore" msgstr "Herstel" #: src/autoload/State.gd: -#, fuzzy msgid "View in Inspector" -msgstr "Bekijken In Lijst" +msgstr "Bekijk in Inspecteur" #: src/autoload/State.gd: msgid "Convert To" @@ -135,26 +134,25 @@ msgstr "6-cijferige hex" #. Refers to a theme preset. #: src/config_classes/SaveData.gd: msgid "Dark" -msgstr "" +msgstr "Donker" #. Refers to a theme preset. #: src/config_classes/SaveData.gd: -#, fuzzy msgid "Light" -msgstr "Hoogte" +msgstr "Helder" #. Refers to a theme preset. #: src/config_classes/SaveData.gd: msgid "Black (OLED)" -msgstr "" +msgstr "Zwart (OLED)" #: src/config_classes/SaveData.gd: msgid "Default Dark" -msgstr "" +msgstr "Standaard Donker" #: src/config_classes/SaveData.gd: msgid "Default Light" -msgstr "" +msgstr "Standaard Helder" #: src/config_classes/TabData.gd: msgid "Empty" @@ -260,13 +258,12 @@ msgid "Snap size" msgstr "Maat inknappen" #: src/ui_parts/display.gd: -#, fuzzy msgid "Paste reference image" -msgstr "Referentiebeeld inladen" +msgstr "Referentiebeeld plakken" #: src/ui_parts/donate_menu.gd: msgid "Links to donation platforms" -msgstr "" +msgstr "Koppelingen naar donatieplatformen" #: 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: @@ -282,6 +279,10 @@ msgstr "Nieuw element" msgid "Dimensions" msgstr "Afmetingen" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Voorbeeldafbeelding maat is beperkt tot {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Maat" @@ -318,10 +319,6 @@ msgstr "Hoogte" 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}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -395,6 +392,10 @@ msgstr "Er bestaat al een bestand genaamt \"{file_name}\". Deze vervangen zal zi msgid "New shape" msgstr "Nieuwe vorm" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Syntaxfout" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Importproblemen" @@ -411,10 +412,6 @@ msgstr "Onherkende element" msgid "Unrecognized attribute" msgstr "Onherkende attribuut" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Syntaxfout" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Element toevoegen" @@ -422,11 +419,11 @@ msgstr "Element toevoegen" #. 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_configuration.gd: msgid "Excluded" -msgstr "" +msgstr "Uitgezonderd" #: src/ui_parts/layout_configuration.gd: msgid "Drag and drop to change the layout" -msgstr "" +msgstr "Sleep en laat los om de indeling te veranderen" #: src/ui_parts/mac_menu.gd: src/ui_widgets/settings_content_shortcuts.gd: msgid "File" @@ -530,23 +527,20 @@ msgid "Update check failed" msgstr "Updatecontrole mislukt" #: src/ui_parts/update_menu.gd: -#, fuzzy msgid "View all releases" -msgstr "Selecteer alle elementen" +msgstr "Bekijk alle releases" #: src/ui_parts/update_menu.gd: msgid "VectorTouch is up-to-date." msgstr "VectorTouch is up-to-date." #: src/ui_parts/update_menu.gd: -#, fuzzy msgid "New versions available!" -msgstr "Nieuwe versies" +msgstr "Nieuwe versies beschikbaar!" #: src/ui_widgets/PanelGrid.gd: -#, fuzzy msgid "Copy email" -msgstr "Pad kopiëren" +msgstr "E-mail kopiëren" #: src/ui_widgets/choose_name_dialog.gd: msgid "Create" @@ -575,7 +569,7 @@ msgstr "Kleurenplukker" #: src/ui_widgets/fps_limit_dropdown.gd: src/ui_widgets/setting_frame.gd: msgid "Unlimited" -msgstr "" +msgstr "Onbeperkt" #: src/ui_widgets/good_color_picker.gd: msgid "Color keywords" @@ -585,6 +579,10 @@ msgstr "kleur trefwoorden" msgid "Eyedropper" msgstr "Druppelaar" +#: src/ui_widgets/palette_config.gd: +msgid "Add new color" +msgstr "Voeg nieuwe kleur toe" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Onbenoemde paletten zullen niet laten zien worden." @@ -644,11 +642,11 @@ msgstr "Ook gebruikt door" #: src/ui_widgets/profile_frame.gd: msgid "Apply" -msgstr "" +msgstr "Toepassen" #: src/ui_widgets/profile_frame.gd: msgid "Apply all of this preset's defaults" -msgstr "" +msgstr "Pas alle standaardwaardes van deze voorinstelling toe" #: src/ui_widgets/setting_frame.gd: src/ui_widgets/setting_shortcut.gd: msgid "Reset to default" @@ -681,9 +679,8 @@ msgid "Preset" msgstr "Voorinstelling" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Determines the default values of the formatter configs." -msgstr "Verandert het maat-factor van de gebruikersinterface." +msgstr "Bepaalt de standaard waarde van de formatter-configuraties." #: src/ui_widgets/settings_content_generic.gd: msgid "Keep comments" @@ -770,28 +767,24 @@ msgid "Remove unnecessary parameters" msgstr "Verwijder onnodige parameters" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Theme preset" -msgstr "Zoom resetten" +msgstr "Thema-preset" #: src/ui_widgets/settings_content_generic.gd: msgid "Determines the default values of theming-related settings, including the highlighter preset." -msgstr "" +msgstr "Bepaalt de standaard waardes voor thema-instellingen, inclusief de markering voorinstelling." #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Primary theme colors" -msgstr "De kleur uitzetten" +msgstr "Primaire thema-kleuren" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Base color" -msgstr "Basiskleuren" +msgstr "Basiskleur" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Accent color" -msgstr "Textkleur" +msgstr "Accentkleur" #: src/ui_widgets/settings_content_generic.gd: msgid "SVG Text colors" @@ -799,11 +792,11 @@ msgstr "SVG tekstkleuren" #: src/ui_widgets/settings_content_generic.gd: msgid "Highlighter preset" -msgstr "" +msgstr "Markeerder voorinstelling" #: src/ui_widgets/settings_content_generic.gd: msgid "Determines the default values of SVG highlighter settings." -msgstr "" +msgstr "Bepaalt de standaardwaardes van SVG markeerder instellingen" #: src/ui_widgets/settings_content_generic.gd: msgid "Symbol color" @@ -840,9 +833,8 @@ msgstr "Fout-kleur" #. Refers to the draggable gizmos. #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Handles" -msgstr "Handvat maat" +msgstr "Hendels" #: src/ui_widgets/settings_content_generic.gd: msgid "Inside color" @@ -865,37 +857,33 @@ msgid "Hovered selected color" msgstr "Zweefde gekozen kleur" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Selection rectangle" -msgstr "Kies een afbeedling" +msgstr "Selectie-vierkant" #: src/ui_widgets/settings_content_generic.gd: msgid "Speed" -msgstr "" +msgstr "Snelheid" #. Refers to the selection rectangle's animated dashed stroke. #: src/ui_widgets/settings_content_generic.gd: msgid "Dash length" -msgstr "" +msgstr "Stippelijn lengte" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Color {index}" -msgstr "Kleurenplukker" +msgstr "Kleur {index}" #: src/ui_widgets/settings_content_generic.gd: msgid "Basic colors" msgstr "Basiskleuren" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Canvas color" -msgstr "Binnenkantkleuren" +msgstr "Canvaskleur" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Grid color" -msgstr "Geldige kleur" +msgstr "Raster kleur" #: src/ui_widgets/settings_content_generic.gd: msgid "Valid color" @@ -915,7 +903,7 @@ msgstr "Sluit tabbladen met de middelste muisknop" #: src/ui_widgets/settings_content_generic.gd: msgid "When enabled, clicking on a tab with the middle mouse button closes it instead of simply focusing it." -msgstr "" +msgstr "Wanneer ingeschakeld, sluit een tabblad bij het klikken van de middeste muisknop in plaats van het simpelweg in focus te brengen." #: src/ui_widgets/settings_content_generic.gd: msgid "Invert zoom direction" @@ -934,43 +922,41 @@ msgid "Use CTRL for zooming" msgstr "Gebruik CTRL om in te zoomen" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "When enabled, scrolling pans the view instead of zooming in. To zoom, hold CTRL while scrolling." -msgstr "Als ingeschakeld, scrollen zal het weergave verschuiven. Om te zoomen,houd CTRL ingedrukt tijdens scrollen." +msgstr "Wanneer ingeschakeld, zal scrollen het weergave verschuiven. Om te zoomen, houd CTRL ingedrukt tijdens het scrollen." #: src/ui_widgets/settings_content_generic.gd: msgid "Display" -msgstr "" +msgstr "Weergave" #: src/ui_widgets/settings_content_generic.gd: msgid "UI scale" msgstr "Gebruiksinterface maat" #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "Determines the scale factor for the interface." -msgstr "Verandert het maat-factor van de gebruikersinterface." +msgstr "Bepaalt de maat-factor van de interface." #. Stands for "Vertical Synchronization". #: src/ui_widgets/settings_content_generic.gd: msgid "V-Sync" -msgstr "" +msgstr "V-Sync" #: src/ui_widgets/settings_content_generic.gd: msgid "Synchronizes graphics rendering with display refresh rate to prevent screen tearing artifacts. May increase input lag slightly." -msgstr "" +msgstr "Synchroniseert grapfische rendering met vernieuwingfrequentie om scherm-scheuring te voorkomen. Kan mogelijk invoer vertraging lichtjes verhogen." #: src/ui_widgets/settings_content_generic.gd: msgid "Maximum FPS" -msgstr "" +msgstr "Maximum FPS" #: src/ui_widgets/settings_content_generic.gd: msgid "Determines the maximum number of frames per second." -msgstr "" +msgstr "Bepaalt de maximum aantal frames per seconde." #: src/ui_widgets/settings_content_generic.gd: msgid "Keep Screen On" -msgstr "" +msgstr "Scherm Aan Houden" #: src/ui_widgets/settings_content_generic.gd: msgid "Miscellaneous" @@ -986,32 +972,31 @@ msgstr "Synchroniseer venstertitel naar bestandsnaam" #: src/ui_widgets/settings_content_generic.gd: msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." -msgstr "" +msgstr "Wanneer ingeschakelt, voegt de huidige bestandsnaam voor de \"VectorTouch\" venstertitel." #: src/ui_widgets/settings_content_generic.gd: msgid "When enabled, uses spaces instead of a single tab for indentation." -msgstr "" +msgstr "Wanneer ingeschakelt, gebruikt spaties in plaats van een tab voor inspringing" #: src/ui_widgets/settings_content_generic.gd: msgid "Warps the cursor to the opposite side whenever it reaches a viewport boundary while panning." -msgstr "Verplaatst de cursur naar de overkant wanneer het buiten de viewport gaat tijdens het rondkijken." +msgstr "Verplaatst de cursur naar de overkant wanneer het buiten het kijkvenster gaat tijdens het rondkijken." #: src/ui_widgets/settings_content_generic.gd: msgid "Keeps the screen on even after inactivity, so the screensaver does not take over." -msgstr "" +msgstr "Houdt het scherm aan, zelfs na inactiviteit, zodat de screensaver niet overneemt." #: src/ui_widgets/settings_content_generic.gd: -#, fuzzy msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." -msgstr "Waneer ingeschakeld, gebruikt jouw besturingssysteem's inheemse bestandendialoog. Wanneer uitgeschakeld, gebruikt VectorTouch's ingebouwde bestandendialoog." +msgstr "Waneer ingeschakeld, gebruikt jouw besturingssysteem's inheemse bestandendialoog in plaats van VectorTouch's ingebouwde bestandendialoog." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting has no effect in the current configuration." -msgstr "" +msgstr "De instellingen heeft geen effect op de huidige configuratie." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting can't be changed on this platform." -msgstr "" +msgstr "De instelling kan niet verandert worden op dit platform." #: src/ui_widgets/settings_content_palettes.gd: msgid "New palette" @@ -1055,36 +1040,35 @@ msgstr "VectorTouch herkent deze attribuut niet" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" -msgstr "" +msgstr "De volgende bestanden zijn verworpen:" #: src/utils/FileUtils.gd: msgid "{file_name} was discarded." -msgstr "" +msgstr "{file_name} is verworpen." #: src/utils/FileUtils.gd: msgid "Discarded files" -msgstr "" +msgstr "Verworpen bestanden" #: src/utils/FileUtils.gd: msgid "Proceed" -msgstr "" +msgstr "Voortgaan" #: src/utils/FileUtils.gd: -#, fuzzy msgid "{file_name} couldn't be opened." -msgstr "Het bestand kon niet worden geopend." +msgstr "{file_name} 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" +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 "" +msgstr "Voortgaan met het importeren van de overige bestanden?" #: src/utils/FileUtils.gd: msgid "Proceed for all files that can't be opened" -msgstr "" +msgstr "Voortgaan voor alle bestanden die niet geopent kunnen worden" #: src/utils/FileUtils.gd: msgid "Save the file?" @@ -1107,9 +1091,8 @@ msgid "Don't save" msgstr "Niet opslaan." #: src/utils/FileUtils.gd: -#, fuzzy msgid "{file_path} is already being edited inside VectorTouch." -msgstr "Het geïmporteerde bestand is al geöpend in VectorTouch" +msgstr "{file_path} wordt al bewerkt in VectorTouch" #: src/utils/FileUtils.gd: msgid "If you want to revert your edits since the last save, use {reset_svg}." @@ -1124,9 +1107,8 @@ msgid "Save SVG" msgstr "SVG opslaan" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Save SVG as" -msgstr "SVG opslaan als…" +msgstr "SVG opslaan als" #: src/utils/TranslationUtils.gd: msgid "Close tabs to the left" @@ -1141,14 +1123,12 @@ msgid "Close all other tabs" msgstr "Kopiëer alle tekst" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Close empty tabs" -msgstr "Tabblad sluiten" +msgstr "Sluit lege tabbladen af" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Close saved tabs" -msgstr "Kopiëer alle tekst" +msgstr "Sluit opgeslagen tabbladen af" #: src/utils/TranslationUtils.gd: msgid "Create tab" @@ -1167,18 +1147,16 @@ msgid "Optimize" msgstr "Optimaliseren" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Optimize SVG" -msgstr "Optimaliseren" +msgstr "SVG optimaliseren" #: src/utils/TranslationUtils.gd: msgid "Copy all text" msgstr "Kopiëer alle tekst" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Copy the SVG text" -msgstr "Kopiëer alle tekst" +msgstr "Kopiëer de SVG teksten" #: src/utils/TranslationUtils.gd: msgid "Reset SVG" @@ -1233,18 +1211,16 @@ msgid "Delete the selection" msgstr "Verwijder de selectie" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Move up" -msgstr "Naar Boven" +msgstr "Omhoog verschuiven" #: src/utils/TranslationUtils.gd: msgid "Move the selection up" msgstr "Beweeg de selectie omhoog" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Move down" -msgstr "Naar Onder" +msgstr "Omlaag verschuiven" #: src/utils/TranslationUtils.gd: msgid "Move the selection down" @@ -1272,7 +1248,7 @@ msgstr "Raster weergeven" #: src/utils/TranslationUtils.gd: msgid "Show handles" -msgstr "Handvaten weergeven" +msgstr "Hendels weergeven" #: src/utils/TranslationUtils.gd: msgid "Show rasterized SVG" @@ -1348,7 +1324,7 @@ msgstr "Applicatie afsluiten" #: src/utils/TranslationUtils.gd: msgid "Toggle fullscreen" -msgstr "" +msgstr "Schakel fullscreen om" #: src/utils/TranslationUtils.gd: msgid "Move to" @@ -1396,41 +1372,36 @@ msgstr "Absoluut" #: src/utils/TranslationUtils.gd: msgid "Code editor" -msgstr "" +msgstr "Code editor" #: src/utils/TranslationUtils.gd: msgid "Inspector" -msgstr "" +msgstr "Inspecteur" #. 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 "Zicht" +msgstr "Kijkvenster" + +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Alleen {extension_list} bestanden zijn ondersteund voor deze operatie." #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Select {format} files" -msgstr "Kies een XML bestand uit" +msgstr "Kies {format} bestanden" #: src/utils/TranslationUtils.gd: msgid "Select an image" msgstr "Kies een afbeedling" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Select an {format} file" -msgstr "Kies een XML bestand uit" +msgstr "Kies een {format} bestand" #: src/utils/TranslationUtils.gd: -#, fuzzy msgid "Save the {format} file" -msgstr "Bewaar het .\"{format}\" bestand" - -#: src/utils/TranslationUtils.gd: -#, fuzzy -msgid "Only {extension_list} files are supported for this operation." -msgstr "Deze bestandsextensie is leeg. Alleen {extension_list} bestanden zijn ondersteund." +msgstr "Bewaar het {format} bestand" #, fuzzy #~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." @@ -1440,7 +1411,7 @@ msgstr "Deze bestandsextensie is leeg. Alleen {extension_list} bestanden zijn on #~ msgstr "Houdt onherkende XML structuren bij" #~ msgid "Handle colors" -#~ msgstr "Handvatkleuren" +#~ msgstr "Hendelkleuren" #~ msgid "Background color" #~ msgstr "Achtergrondskleur" @@ -1493,7 +1464,7 @@ msgstr "Deze bestandsextensie is leeg. Alleen {extension_list} bestanden zijn on #~ msgstr "Wisselt in- en uitzoemen met de muiswiel." #~ msgid "Wraps the mouse cursor around when panning the viewport." -#~ msgstr "Wikkelt de muiscursor rond tijdens het verschuiven van de kijkvenster." +#~ msgstr "Wikkelt de muiscursor rond tijdens het verschuiven van het kijkvenster." #~ msgid "This requires VectorTouch to connect to the internet." #~ msgstr "Hiervoor moet VectorTouch verbinding maken met het internet." diff --git a/translations/pt_BR.po b/translations/pt_BR.po index b2858e0..4713459 100644 --- a/translations/pt_BR.po +++ b/translations/pt_BR.po @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: VectorTouch\n" +"Project-Id-Version: GodSVG\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: \n" @@ -18,12 +18,12 @@ msgid "translation-credits" msgstr "Felipe Sena Costa " #: src/autoload/HandlerGUI.gd: -msgid "Quit VectorTouch" -msgstr "Sair do VectorTouch" +msgid "Quit GodSVG" +msgstr "Sair do GodSVG" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to quit VectorTouch?" -msgstr "Você deseja sair do VectorTouch?" +msgid "Do you want to quit GodSVG?" +msgstr "Você deseja sair do GodSVG?" #: src/autoload/HandlerGUI.gd: msgid "Quit" @@ -41,6 +41,14 @@ msgstr "Isso irá conectar ao github.com para comparar o número de versionament msgid "OK" msgstr "OK" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Esse gráfico pode ser exportado apenas como um SVG pois as suas proporções são extremas." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Esse gráfico pode ser exportado apenas como SVG pois o seu tamanho não foi definido." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Você deseja prosseguir?" @@ -54,14 +62,6 @@ msgstr "Exportar SVG" msgid "Export" msgstr "Exportar" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Esse gráfico pode ser exportado apenas como um SVG pois as suas proporções são extremas." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Esse gráfico pode ser exportado apenas como SVG pois o seu tamanho não foi definido." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -281,6 +281,10 @@ msgstr "Novo elemento" msgid "Dimensions" msgstr "Dimensões" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "O tamanho da visualização da prévia de imagem é limitado à {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Tamanho" @@ -317,10 +321,6 @@ msgstr "Altura" msgid "Copy" msgstr "Copiar" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "O tamanho da visualização da prévia de imagem é limitado à {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -394,6 +394,10 @@ msgstr "Um arquivo nomeado \"{file_name}\" já existe. Substituir este arquivo i msgid "New shape" msgstr "Nova forma" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Erro de sintaxe" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Problemas de Importação" @@ -410,10 +414,6 @@ msgstr "Elemento não reconhecido" msgid "Unrecognized attribute" msgstr "Atributo não reconhecido" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Erro de sintaxe" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Adicionar elemento" @@ -533,8 +533,8 @@ msgid "View all releases" msgstr "Ver todos os lançamentos" #: src/ui_parts/update_menu.gd: -msgid "VectorTouch is up-to-date." -msgstr "VectorTouch está atualizado." +msgid "GodSVG is up-to-date." +msgstr "GodSVG está atualizado." #: src/ui_parts/update_menu.gd: msgid "New versions available!" @@ -582,6 +582,11 @@ msgstr "Palavras-chave de cor" msgid "Eyedropper" msgstr "Conta-gotas" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Cor de texto" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Paletas sem nome não serão mostradas." @@ -663,7 +668,7 @@ msgstr "Adicionar atalho" msgid "Press keys…" msgstr "Pressione as teclas…" -#. Refers to the formatter used for VectorTouch's code editor. +#. Refers to the formatter used for GodSVG's code editor. #: src/ui_widgets/settings_content_generic.gd: msgid "Editor formatter" msgstr "Formatador do editor" @@ -981,7 +986,7 @@ msgid "Sync window title to file name" msgstr "Sincronizar título de janela com o nome do arquivo" #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." +msgid "When enabled, adds the current file name before the \"GodSVG\" window title." msgstr "" #: src/ui_widgets/settings_content_generic.gd: @@ -998,8 +1003,8 @@ msgstr "" #: src/ui_widgets/settings_content_generic.gd: #, fuzzy -msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." -msgstr "Se habilitado, usa o diálogo de arquivo nativo do sistema operacional. Se desabilitado, usa o diálogo de arquivo embutido no VectorTouch." +msgid "When enabled, uses your operating system's native file dialog instead of GodSVG's built-in one." +msgstr "Se habilitado, usa o diálogo de arquivo nativo do sistema operacional. Se desabilitado, usa o diálogo de arquivo embutido no GodSVG." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting has no effect in the current configuration." @@ -1046,8 +1051,8 @@ msgid "New transform" msgstr "Nova transformação" #: src/ui_widgets/unrecognized_field.gd: -msgid "VectorTouch doesn’t recognize this attribute" -msgstr "O VectorTouch não reconhece este atributo" +msgid "GodSVG doesn’t recognize this attribute" +msgstr "O GodSVG não reconhece este atributo" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" @@ -1102,8 +1107,8 @@ msgid "Don't save" msgstr "Não salvar" #: src/utils/FileUtils.gd: -msgid "{file_path} is already being edited inside VectorTouch." -msgstr "{file_path} já está sendo editado dentro do VectorTouch." +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}." @@ -1310,20 +1315,20 @@ msgid "Open Donate menu" msgstr "Abrir o menu Doar" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch repository" -msgstr "Repositório do VectorTouch" +msgid "GodSVG repository" +msgstr "Repositório do GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch repository" -msgstr "Abrir o repositório do VectorTouch" +msgid "Open GodSVG repository" +msgstr "Abrir o repositório do GodSVG" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch website" -msgstr "Website do VectorTouch" +msgid "GodSVG website" +msgstr "Website do GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch website" -msgstr "Abrir website do VectorTouch" +msgid "Open GodSVG website" +msgstr "Abrir website do GodSVG" #: src/utils/TranslationUtils.gd: msgid "Check for updates" @@ -1394,6 +1399,10 @@ msgstr "Inspetor" msgid "Viewport" msgstr "Canvas" +#: src/utils/TranslationUtils.gd: +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." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Selecionar arquivos do tipo {format}" @@ -1410,12 +1419,8 @@ msgstr "Selecionar um arquivo do tipo {format}" msgid "Save the {format} file" msgstr "Salvar o arquivo do tipo {format}" -#: src/utils/TranslationUtils.gd: -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 "If turned off, the window title remains as \"VectorTouch\" without including the current file." -#~ msgstr "Se desabilitado, o título da janela permanecerá como \"VectorTouch\" sem incluir o nome do arquivo aberto." +#~ 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\" sem incluir o nome do arquivo aberto." #~ msgid "Keep unrecognized XML structures" #~ msgstr "Manter estrutura XML não reconhecida" diff --git a/translations/ru.po b/translations/ru.po index 7ba3345..317efcb 100644 --- a/translations/ru.po +++ b/translations/ru.po @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: VectorTouch\n" +"Project-Id-Version: GodSVG\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: freeducks-debug \n" @@ -19,12 +19,12 @@ msgid "translation-credits" msgstr "volkov, Gallifreyan , freeducks-debug" #: src/autoload/HandlerGUI.gd: -msgid "Quit VectorTouch" -msgstr "Выйти из VectorTouch" +msgid "Quit GodSVG" +msgstr "Выйти из GodSVG" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to quit VectorTouch?" -msgstr "Вы хотите выйти из VectorTouch?" +msgid "Do you want to quit GodSVG?" +msgstr "Вы хотите выйти из GodSVG?" #: src/autoload/HandlerGUI.gd: msgid "Quit" @@ -42,6 +42,14 @@ msgstr "Требуется подключение к github.com, чтобы ср msgid "OK" msgstr "Хорошо" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Изображение можно экспортировать только как SVG, поскольку его пропорции слишком экстремальные." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Изображение можно экспортировать только как SVG, поскольку его размеры не заданы." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Продолжить?" @@ -55,14 +63,6 @@ msgstr "Экспортировать SVG" msgid "Export" msgstr "Экспортовать" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Изображение можно экспортировать только как SVG, поскольку его пропорции слишком экстремальные." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Изображение можно экспортировать только как SVG, поскольку его размеры не заданы." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -280,6 +280,10 @@ msgstr "Новый элемент" msgid "Dimensions" msgstr "Размеры" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Размер предварительного просмотра изображения ограничен до {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Размер" @@ -316,10 +320,6 @@ msgstr "Высота" msgid "Copy" msgstr "Скопировать" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "Размер предварительного просмотра изображения ограничен до {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -393,6 +393,10 @@ msgstr "Файл с названием \"{file_name}\" уже существуе msgid "New shape" msgstr "Новая привязка" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Синтаксическая ошибка" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Проблемы с импортированием" @@ -409,10 +413,6 @@ msgstr "Неизвестный элемент" msgid "Unrecognized attribute" msgstr "Неизвестный атрибут" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Синтаксическая ошибка" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Добавить элемент" @@ -532,8 +532,8 @@ msgid "View all releases" msgstr "Показать все сборки" #: src/ui_parts/update_menu.gd: -msgid "VectorTouch is up-to-date." -msgstr "Установлена последняя версия VectorTouch." +msgid "GodSVG is up-to-date." +msgstr "Установлена последняя версия GodSVG." #: src/ui_parts/update_menu.gd: msgid "New versions available!" @@ -580,6 +580,11 @@ msgstr "Ключевые слова цвета" msgid "Eyedropper" msgstr "Пипетка" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Цвет акцента" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Палитры без названия не будут отображаться." @@ -661,7 +666,7 @@ msgstr "Добавить сочетание клавиш" msgid "Press keys…" msgstr "Нажмите клавиши…" -#. Refers to the formatter used for VectorTouch's code editor. +#. Refers to the formatter used for GodSVG's code editor. #: src/ui_widgets/settings_content_generic.gd: msgid "Editor formatter" msgstr "Форматер редактора" @@ -968,8 +973,8 @@ msgid "Sync window title to file name" msgstr "Синхронизировать заголовок окна с названием файла" #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." -msgstr "Когда активно, добавит текущий открытый файл перед \"VectorTouch\" в заголовке окна." +msgid "When enabled, adds the current file name before the \"GodSVG\" window title." +msgstr "Когда активно, добавит текущий открытый файл перед \"GodSVG\" в заголовке окна." #: src/ui_widgets/settings_content_generic.gd: msgid "When enabled, uses spaces instead of a single tab for indentation." @@ -984,8 +989,8 @@ msgid "Keeps the screen on even after inactivity, so the screensaver does not ta msgstr "Будет удерживать экран активным, даже после бездействия, поэтому хранитель экрана не будет запускаться." #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." -msgstr "Если включено, то будет использоваться родной файловый диалог вашей операционной системы для выбора файлов вместо встроенного в VectorTouch." +msgid "When enabled, uses your operating system's native file dialog instead of GodSVG's built-in one." +msgstr "Если включено, то будет использоваться родной файловый диалог вашей операционной системы для выбора файлов вместо встроенного в GodSVG." #: src/ui_widgets/settings_content_generic.gd: msgid "The setting has no effect in the current configuration." @@ -1032,8 +1037,8 @@ msgid "New transform" msgstr "Новая трансформация" #: src/ui_widgets/unrecognized_field.gd: -msgid "VectorTouch doesn’t recognize this attribute" -msgstr "VectorTouch не может распознать этот атрибут" +msgid "GodSVG doesn’t recognize this attribute" +msgstr "GodSVG не может распознать этот атрибут" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" @@ -1088,8 +1093,8 @@ msgid "Don't save" msgstr "Не сохранять" #: src/utils/FileUtils.gd: -msgid "{file_path} is already being edited inside VectorTouch." -msgstr "Файл {file_path} уже редактируется внутри VectorTouch." +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}." @@ -1296,20 +1301,20 @@ msgid "Open Donate menu" msgstr "Открыть меню благотворителей" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch repository" -msgstr "Репозиторий VectorTouch" +msgid "GodSVG repository" +msgstr "Репозиторий GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch repository" -msgstr "Открыть репозиторий VectorTouch" +msgid "Open GodSVG repository" +msgstr "Открыть репозиторий GodSVG" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch website" -msgstr "Веб-сайт VectorTouch" +msgid "GodSVG website" +msgstr "Веб-сайт GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch website" -msgstr "Открыть веб-сайт VectorTouch" +msgid "Open GodSVG website" +msgstr "Открыть веб-сайт GodSVG" #: src/utils/TranslationUtils.gd: msgid "Check for updates" @@ -1380,6 +1385,10 @@ msgstr "Инспектор" msgid "Viewport" msgstr "Окно просмотра" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Только файлы с расширениями {extension_list} поддерживаются для этой операции." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Выбрать {format} файлы" @@ -1396,12 +1405,8 @@ msgstr "Выбрать {format} файл" msgid "Save the {format} file" msgstr "Сохранить {format} файл" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Только файлы с расширениями {extension_list} поддерживаются для этой операции." - -#~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." -#~ msgstr "Если выключено - заголовок окна будет просто \"VectorTouch\", не включая текущий файл." +#~ msgid "If turned off, the window title remains as \"GodSVG\" without including the current file." +#~ msgstr "Если выключено - заголовок окна будет просто \"GodSVG\", не включая текущий файл." #~ msgid "Keep unrecognized XML structures" #~ msgstr "Сохранять нераспознанные XML" diff --git a/translations/uk.po b/translations/uk.po index cc815c8..4b481b1 100644 --- a/translations/uk.po +++ b/translations/uk.po @@ -1,7 +1,7 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: VectorTouch\n" +"Project-Id-Version: GodSVG\n" "POT-Creation-Date: \n" "PO-Revision-Date: \n" "Last-Translator: freeducks-debug \n" @@ -18,12 +18,12 @@ msgid "translation-credits" msgstr "volkov, freeducks-debug" #: src/autoload/HandlerGUI.gd: -msgid "Quit VectorTouch" -msgstr "Вийти із VectorTouch" +msgid "Quit GodSVG" +msgstr "Вийти із GodSVG" #: src/autoload/HandlerGUI.gd: -msgid "Do you want to quit VectorTouch?" -msgstr "Ви дійсно бажаєте вийти із VectorTouch?" +msgid "Do you want to quit GodSVG?" +msgstr "Ви дійсно бажаєте вийти із GodSVG?" #: src/autoload/HandlerGUI.gd: msgid "Quit" @@ -41,6 +41,14 @@ msgstr "Це потребує з'єднання з github.com, щоб порів msgid "OK" msgstr "Добре" +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "Це зображення може бути експортоване тільки як SVG, оскільки його пропорції занадто екстремальні." + +#: src/autoload/HandlerGUI.gd: +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "Це зображення може бути експортоване тільки як SVG, оскільки його розмір не завданий." + #: src/autoload/HandlerGUI.gd: msgid "Do you want to proceed?" msgstr "Ви дійсно бажаєте продовжити?" @@ -54,14 +62,6 @@ msgstr "Експортувати SVG" msgid "Export" msgstr "Експортувати" -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "Це зображення може бути експортоване тільки як SVG, оскільки його пропорції занадто екстремальні." - -#: src/autoload/HandlerGUI.gd: -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "Це зображення може бути експортоване тільки як SVG, оскільки його розмір не завданий." - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -279,6 +279,10 @@ msgstr "Новий елемент" msgid "Dimensions" msgstr "Розміри" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "Розмір попереднього перегляду зображення обмежено до {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "Розмір" @@ -315,10 +319,6 @@ msgstr "Висота" msgid "Copy" msgstr "Скопіювати" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "Розмір попереднього перегляду зображення обмежено до {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -392,6 +392,10 @@ msgstr "Файл з назваою \"{file_name}\" вже існує. Його msgid "New shape" msgstr "Нова форма" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "Синтаксична помилка" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "Проблема з імпортуванням" @@ -408,10 +412,6 @@ msgstr "Невідомий елемент" msgid "Unrecognized attribute" msgstr "Невідомий атрибут" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "Синтаксична помилка" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "Додати елемент" @@ -531,8 +531,8 @@ msgid "View all releases" msgstr "Показати усі версії" #: src/ui_parts/update_menu.gd: -msgid "VectorTouch is up-to-date." -msgstr "VectorTouch оновлено до останньої версії." +msgid "GodSVG is up-to-date." +msgstr "GodSVG оновлено до останньої версії." #: src/ui_parts/update_menu.gd: msgid "New versions available!" @@ -579,6 +579,11 @@ msgstr "Ключові слова кольорів" msgid "Eyedropper" msgstr "Піпетка" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "Колір акценту" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "Палітри без назв не будуть відображатися." @@ -660,7 +665,7 @@ msgstr "Додати клавіатурне скорочення" msgid "Press keys…" msgstr "Натисніть кнопки…" -#. Refers to the formatter used for VectorTouch's code editor. +#. Refers to the formatter used for GodSVG's code editor. #: src/ui_widgets/settings_content_generic.gd: msgid "Editor formatter" msgstr "Форматер редактору" @@ -971,8 +976,8 @@ msgid "Sync window title to file name" msgstr "Синхронізувати заголовок вікна з назвою поточного файлу" #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, adds the current file name before the \"VectorTouch\" window title." -msgstr "Коли активовано, додає назву поточного файлу перед \"VectorTouch\" у заголовку вікна." +msgid "When enabled, adds the current file name before the \"GodSVG\" window title." +msgstr "Коли активовано, додає назву поточного файлу перед \"GodSVG\" у заголовку вікна." #: src/ui_widgets/settings_content_generic.gd: msgid "When enabled, uses spaces instead of a single tab for indentation." @@ -987,7 +992,7 @@ msgid "Keeps the screen on even after inactivity, so the screensaver does not ta msgstr "Відкладати присипляння системи та/або екрану навіть після бездіяльності, завдяки чому зберігач екрану не буде запускатися." #: src/ui_widgets/settings_content_generic.gd: -msgid "When enabled, uses your operating system's native file dialog instead of VectorTouch's built-in one." +msgid "When enabled, uses your operating system's native file dialog instead of GodSVG's built-in one." msgstr "Якщо увімкнено, то буде використовувати рідний файловий діалог вашої операційної системи для обирання файлів замість вбудованого." #: src/ui_widgets/settings_content_generic.gd: @@ -1035,8 +1040,8 @@ msgid "New transform" msgstr "Нова трансформація" #: src/ui_widgets/unrecognized_field.gd: -msgid "VectorTouch doesn’t recognize this attribute" -msgstr "VectorTouch не може розпізнати цей атрибут" +msgid "GodSVG doesn’t recognize this attribute" +msgstr "GodSVG не може розпізнати цей атрибут" #: src/utils/FileUtils.gd: msgid "The following files were discarded:" @@ -1091,8 +1096,8 @@ msgid "Don't save" msgstr "Не зберігати" #: src/utils/FileUtils.gd: -msgid "{file_path} is already being edited inside VectorTouch." -msgstr "Файл {file_path} вже редагується в VectorTouch." +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}." @@ -1300,20 +1305,20 @@ msgid "Open Donate menu" msgstr "Відкрити меню благодійників" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch repository" -msgstr "Репозиторій VectorTouch" +msgid "GodSVG repository" +msgstr "Репозиторій GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch repository" -msgstr "Відкрити репозиторій VectorTouch" +msgid "Open GodSVG repository" +msgstr "Відкрити репозиторій GodSVG" #: src/utils/TranslationUtils.gd: -msgid "VectorTouch website" -msgstr "Веб-сайт VectorTouch" +msgid "GodSVG website" +msgstr "Веб-сайт GodSVG" #: src/utils/TranslationUtils.gd: -msgid "Open VectorTouch website" -msgstr "Відкрити веб-сайт VectorTouch" +msgid "Open GodSVG website" +msgstr "Відкрити веб-сайт GodSVG" #: src/utils/TranslationUtils.gd: msgid "Check for updates" @@ -1384,6 +1389,10 @@ msgstr "Інспектор" msgid "Viewport" msgstr "Вікно перегляду" +#: src/utils/TranslationUtils.gd: +msgid "Only {extension_list} files are supported for this operation." +msgstr "Тільки файли з розширеннями {extension_list} підтримуються для цієї операції." + #: src/utils/TranslationUtils.gd: msgid "Select {format} files" msgstr "Обрати {format} файли" @@ -1400,12 +1409,8 @@ msgstr "Обрати {format} файл" msgid "Save the {format} file" msgstr "Зберегти {format} файл" -#: src/utils/TranslationUtils.gd: -msgid "Only {extension_list} files are supported for this operation." -msgstr "Тільки файли з розширеннями {extension_list} підтримуються для цієї операції." - -#~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." -#~ msgstr "Якщо вимкнено - заголовок вікна залишиться як \"VectorTouch\", не включаючи назву поточного файлу." +#~ msgid "If turned off, the window title remains as \"GodSVG\" without including the current file." +#~ msgstr "Якщо вимкнено - заголовок вікна залишиться як \"GodSVG\", не включаючи назву поточного файлу." #~ msgid "Keep unrecognized XML structures" #~ msgstr "Зберігати не розпізнані структури XML" diff --git a/translations/zh_CN.po b/translations/zh_CN.po index 07a3031..6be2b33 100644 --- a/translations/zh_CN.po +++ b/translations/zh_CN.po @@ -41,6 +41,16 @@ msgstr "" msgid "OK" msgstr "确定" +#: src/autoload/HandlerGUI.gd: +#, fuzzy +msgid "The graphic can be exported only as SVG because its proportions are too extreme." +msgstr "此图像只能以 SVG 格式导出,因为其大小未定义。您确定要继续吗?" + +#: src/autoload/HandlerGUI.gd: +#, fuzzy +msgid "The graphic can be exported only as SVG because its size is not defined." +msgstr "此图像只能以 SVG 格式导出,因为其大小未定义。您确定要继续吗?" + #: src/autoload/HandlerGUI.gd: #, fuzzy msgid "Do you want to proceed?" @@ -55,16 +65,6 @@ msgstr "导出 SVG" msgid "Export" msgstr "导出" -#: src/autoload/HandlerGUI.gd: -#, fuzzy -msgid "The graphic can be exported only as SVG because its proportions are too extreme." -msgstr "此图像只能以 SVG 格式导出,因为其大小未定义。您确定要继续吗?" - -#: src/autoload/HandlerGUI.gd: -#, fuzzy -msgid "The graphic can be exported only as SVG because its size is not defined." -msgstr "此图像只能以 SVG 格式导出,因为其大小未定义。您确定要继续吗?" - #: src/autoload/State.gd: src/ui_parts/good_file_dialog.gd: #: src/ui_widgets/alert_dialog.gd: src/utils/FileUtils.gd: msgid "Alert!" @@ -287,6 +287,10 @@ msgstr "新元素" msgid "Dimensions" msgstr "大小" +#: src/ui_parts/export_menu.gd: +msgid "Preview image size is limited to {dimensions}" +msgstr "预览图像大小被限制到 {dimensions}" + #: src/ui_parts/export_menu.gd: src/ui_widgets/settings_content_generic.gd: msgid "Size" msgstr "大小" @@ -323,10 +327,6 @@ msgstr "高度" msgid "Copy" msgstr "复制" -#: src/ui_parts/export_menu.gd: -msgid "Preview image size is limited to {dimensions}" -msgstr "预览图像大小被限制到 {dimensions}" - #: src/ui_parts/global_actions.gd: src/ui_parts/layout_configuration.gd: #: src/ui_parts/shortcut_panel_config.gd: msgid "Layout" @@ -400,6 +400,10 @@ msgstr "文件 “{file_name}” 已经存在。替换操作将会覆写它的 msgid "New shape" msgstr "新形状" +#: src/ui_parts/import_warning_menu.gd: +msgid "Syntax error" +msgstr "语法错误" + #: src/ui_parts/import_warning_menu.gd: msgid "Import Problems" msgstr "导入出错" @@ -416,10 +420,6 @@ msgstr "未知元素" msgid "Unrecognized attribute" msgstr "未知属性" -#: src/ui_parts/import_warning_menu.gd: -msgid "Syntax error" -msgstr "语法错误" - #: src/ui_parts/inspector.gd: msgid "Add element" msgstr "添加元素" @@ -596,6 +596,11 @@ msgstr "取色器" msgid "Eyedropper" msgstr "" +#: src/ui_widgets/palette_config.gd: +#, fuzzy +msgid "Add new color" +msgstr "添加新元素" + #: src/ui_widgets/palette_config.gd: msgid "Unnamed palettes won't be shown." msgstr "未命名的调色盘不会被显示。" @@ -1430,6 +1435,11 @@ msgstr "" msgid "Viewport" msgstr "视图" +#: src/utils/TranslationUtils.gd: +#, fuzzy +msgid "Only {extension_list} files are supported for this operation." +msgstr "文件扩展名为空。只有 {extension_list} 内的文件受支持。" + #: src/utils/TranslationUtils.gd: #, fuzzy msgid "Select {format} files" @@ -1449,11 +1459,6 @@ msgstr "选择 XML 文件" msgid "Save the {format} file" msgstr "保存 .\"{format}\" 文件" -#: src/utils/TranslationUtils.gd: -#, fuzzy -msgid "Only {extension_list} files are supported for this operation." -msgstr "文件扩展名为空。只有 {extension_list} 内的文件受支持。" - #, fuzzy #~ msgid "If turned off, the window title remains as \"VectorTouch\" without including the current file." #~ msgstr "如果禁用,不论当前打开的文件是什么,窗口标题都将固定为 “VectorTouch”。" @@ -1522,10 +1527,6 @@ msgstr "文件扩展名为空。只有 {extension_list} 内的文件受支持。 #~ msgid "This requires VectorTouch to connect to the internet." #~ msgstr "这需要 VectorTouch 连接互联网。" -#, fuzzy -#~ msgid "Add new tab" -#~ msgstr "添加新元素" - #~ msgid "Open file" #~ msgstr "打开文件" From f8b512b95b0c86aa598b53a041e3c98032afa0b4 Mon Sep 17 00:00:00 2001 From: Anish Mishra Date: Sun, 17 Aug 2025 22:03:13 +0530 Subject: [PATCH 2/3] Update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7c82308..09d47a2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Godot-specific ignores .godot/ .nomedia +android/ # Others .~lock.* From 7809340f609329747c34e9a65a4cc674d9e51a1b Mon Sep 17 00:00:00 2001 From: Anish Mishra Date: Sun, 17 Aug 2025 22:12:50 +0530 Subject: [PATCH 3/3] Update export_presets.cfg --- export_presets.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/export_presets.cfg b/export_presets.cfg index 25481df..87879b9 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -36,7 +36,7 @@ architectures/armeabi-v7a=true architectures/arm64-v8a=true architectures/x86=false architectures/x86_64=false -version/code=4 +version/code=5 version/name="1.0-alpha4-dev" package/unique_name="com.vectortouch.app" package/name="VectorTouch" @@ -261,8 +261,8 @@ architectures/armeabi-v7a=true architectures/arm64-v8a=true architectures/x86=true architectures/x86_64=true -version/code=4 -version/name="1.0-alpha3-dev" +version/code=5 +version/name="1.0-alpha4-dev" package/unique_name="com.vectortouch.app" package/name="VectorTouch" package/signed=true