From 9f8dd1ff938327deee159246bb7009648a04313f Mon Sep 17 00:00:00 2001 From: Mew Pur Pur <85438892+MewPurPur@users.noreply.github.com> Date: Tue, 9 Apr 2024 02:33:52 +0300 Subject: [PATCH] Improve the fallback file dialog (#641) --- src/GlobalSettings.gd | 11 +- src/HandlerGUI.gd | 2 +- src/SVG.gd | 52 +++- src/ThemeGenerator.gd | 37 ++- src/data_classes/SaveData.gd | 1 + src/ui_parts/alert_dialog.tscn | 20 +- src/ui_parts/export_dialog.gd | 33 +-- src/ui_parts/export_dialog.tscn | 3 +- src/ui_parts/good_file_dialog.gd | 289 +++++++++++++++++++ src/ui_parts/good_file_dialog.tscn | 205 +++++++++++++ src/ui_parts/import_warning_dialog.tscn | 34 +-- src/ui_parts/settings_menu.gd | 18 +- src/ui_parts/settings_menu.tscn | 32 +- src/ui_parts/svg_file_dialog.gd | 10 - src/ui_parts/svg_file_dialog.tscn | 19 -- translations/GodSVG.pot | 30 ++ translations/bg.po | 32 ++ translations/de.po | 37 +++ translations/en.po | 32 ++ translations/ru.po | 37 +++ translations/uk.po | 37 +++ visual/icon.svg | 2 +- visual/icons/DirDesktop.svg | 1 + visual/icons/DirDesktop.svg.import | 37 +++ visual/icons/DirDocuments.svg | 1 + visual/icons/DirDocuments.svg.import | 37 +++ visual/icons/DirDownloads.svg | 1 + visual/icons/DirDownloads.svg.import | 37 +++ visual/icons/DirMovies.svg | 1 + visual/icons/DirMovies.svg.import | 37 +++ visual/icons/DirMusic.svg | 1 + visual/icons/DirMusic.svg.import | 37 +++ visual/icons/DirPictures.svg | 1 + visual/icons/DirPictures.svg.import | 37 +++ visual/icons/Folder.svg | 1 + visual/icons/Folder.svg.import | 37 +++ visual/icons/{theme => }/FolderUp.svg | 0 visual/icons/{theme => }/FolderUp.svg.import | 6 +- visual/icons/Search.svg | 1 + visual/icons/Search.svg.import | 37 +++ visual/icons/tag/polyline.svg | 2 +- 41 files changed, 1161 insertions(+), 124 deletions(-) create mode 100644 src/ui_parts/good_file_dialog.gd create mode 100644 src/ui_parts/good_file_dialog.tscn delete mode 100644 src/ui_parts/svg_file_dialog.gd delete mode 100644 src/ui_parts/svg_file_dialog.tscn create mode 100644 visual/icons/DirDesktop.svg create mode 100644 visual/icons/DirDesktop.svg.import create mode 100644 visual/icons/DirDocuments.svg create mode 100644 visual/icons/DirDocuments.svg.import create mode 100644 visual/icons/DirDownloads.svg create mode 100644 visual/icons/DirDownloads.svg.import create mode 100644 visual/icons/DirMovies.svg create mode 100644 visual/icons/DirMovies.svg.import create mode 100644 visual/icons/DirMusic.svg create mode 100644 visual/icons/DirMusic.svg.import create mode 100644 visual/icons/DirPictures.svg create mode 100644 visual/icons/DirPictures.svg.import create mode 100644 visual/icons/Folder.svg create mode 100644 visual/icons/Folder.svg.import rename visual/icons/{theme => }/FolderUp.svg (100%) rename visual/icons/{theme => }/FolderUp.svg.import (74%) create mode 100644 visual/icons/Search.svg create mode 100644 visual/icons/Search.svg.import diff --git a/src/GlobalSettings.gd b/src/GlobalSettings.gd index f294ee5f..9756728d 100644 --- a/src/GlobalSettings.gd +++ b/src/GlobalSettings.gd @@ -56,6 +56,7 @@ const default_config = { "invert_zoom": false, "wrap_mouse": false, "use_ctrl_for_zoom": true, + "use_native_file_dialog": true, }, } @@ -73,10 +74,6 @@ var language: String: var palettes: Array[ColorPalette] = [] -# Input -var invert_zoom := false -var wrap_mouse := false -var use_ctrl_for_zoom := true # Autoformat var general_number_precision := 3 @@ -118,6 +115,12 @@ var basic_color_valid := Color("9f9") var basic_color_error := Color("f99") var basic_color_warning := Color("ff9") +# Other +var invert_zoom := false +var wrap_mouse := false +var use_ctrl_for_zoom := true +var use_native_file_dialog := true + func toggle_bool_setting(section: String, setting: String) -> void: set(setting, !get(setting)) diff --git a/src/HandlerGUI.gd b/src/HandlerGUI.gd index ab922378..951f1af6 100644 --- a/src/HandlerGUI.gd +++ b/src/HandlerGUI.gd @@ -40,9 +40,9 @@ func add_overlay(overlay_menu: Node) -> void: overlay_ref = OverlayRect.new() get_tree().get_root().add_child(overlay_ref) overlay_ref.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + overlay_ref.add_child(overlay_menu) if overlay_menu is Control: overlay_menu.set_anchors_and_offsets_preset(Control.PRESET_CENTER) - overlay_ref.add_child(overlay_menu) overlay_menu.tree_exiting.connect(remove_overlay) has_overlay = true get_tree().paused = true diff --git a/src/SVG.gd b/src/SVG.gd index 25160eae..1e1246fb 100644 --- a/src/SVG.gd +++ b/src/SVG.gd @@ -2,9 +2,11 @@ ## The SVG text, and the native [TagSVG] representation. extends Node +const GoodFileDialogType = preload("res://src/ui_parts/good_file_dialog.gd") + const AlertDialog := preload("res://src/ui_parts/alert_dialog.tscn") const ImportWarningDialog = preload("res://src/ui_parts/import_warning_dialog.tscn") -const SVGFileDialog = preload("res://src/ui_parts/svg_file_dialog.tscn") +const GoodFileDialog = preload("res://src/ui_parts/good_file_dialog.tscn") const ExportDialog = preload("res://src/ui_parts/export_dialog.tscn") const default = '' @@ -78,16 +80,22 @@ func _on_undo_redo() -> void: func refresh() -> void: SVG.root_tag.replace_self(SVG.root_tag.create_duplicate()) + +func is_native_preferred() -> bool: + return DisplayServer.has_feature(DisplayServer.FEATURE_NATIVE_DIALOG) and\ + GlobalSettings.use_native_file_dialog + func open_import_dialog() -> void: # Open it inside a native file dialog, or our custom one if it's not available. - if DisplayServer.has_feature(DisplayServer.FEATURE_NATIVE_DIALOG): + if is_native_preferred(): DisplayServer.file_dialog_show("Import a .svg file", Utils.get_last_dir(), "", false, DisplayServer.FILE_DIALOG_MODE_OPEN_FILE, ["*.svg"], native_file_import) elif OS.has_feature("web"): HandlerGUI.web_load_svg() else: - var svg_import_dialog := SVGFileDialog.instantiate() - svg_import_dialog.current_dir = Utils.get_last_dir() + var svg_import_dialog := GoodFileDialog.instantiate() + svg_import_dialog.setup(Utils.get_last_dir(), "", + GoodFileDialogType.FileMode.SELECT, "svg") HandlerGUI.add_overlay(svg_import_dialog) svg_import_dialog.file_selected.connect(apply_svg_from_path) @@ -103,7 +111,7 @@ func open_export_dialog() -> void: func open_save_dialog(extension: String, native_callable: Callable, non_native_callable: Callable) -> void: # Open it inside a native file dialog, or our custom one if it's not available. - if DisplayServer.has_feature(DisplayServer.FEATURE_NATIVE_DIALOG): + if is_native_preferred(): DisplayServer.file_dialog_show("Save the .%s file" % extension, Utils.get_last_dir(), Utils.get_file_name(GlobalSettings.save_data.current_file_path) + "." + extension, @@ -112,9 +120,10 @@ non_native_callable: Callable) -> void: elif OS.has_feature("web"): HandlerGUI.web_save_svg() else: - var svg_export_dialog := SVGFileDialog.instantiate() - svg_export_dialog.current_dir = Utils.get_last_dir() - svg_export_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE + var svg_export_dialog := GoodFileDialog.instantiate() + svg_export_dialog.setup(Utils.get_last_dir(), + Utils.get_file_name(GlobalSettings.save_data.current_file_path), + GoodFileDialogType.FileMode.SAVE, extension) HandlerGUI.add_overlay(svg_export_dialog) svg_export_dialog.file_selected.connect(non_native_callable) @@ -155,10 +164,37 @@ func apply_svg_from_path(path: String) -> int: HandlerGUI.add_overlay(warning_panel) return OK +func generate_image_from_tags(upscale_amount := 1.0) -> Image: + var export_svg := SVG.root_tag.create_duplicate() + if export_svg.attributes.viewBox.get_list().is_empty(): + export_svg.attributes.viewBox.set_list([0, 0, export_svg.width, export_svg.height]) + export_svg.attributes.width.set_num(export_svg.width * upscale_amount) + export_svg.attributes.height.set_num(export_svg.height * upscale_amount) + var img := Image.new() + img.load_svg_from_string(SVGParser.svg_to_text(export_svg)) + img.fix_alpha_edges() # See godot issue 82579. + return img + + func finish_import(svg_text: String, file_path: String) -> void: GlobalSettings.modify_save_data("current_file_path", file_path) apply_svg_text(svg_text) +func finish_export(file_path: String, extension: String, upscale_amount := 1.0) -> void: + if file_path.get_extension().is_empty(): + file_path += "." + extension + + GlobalSettings.modify_save_data("last_used_dir", file_path.get_base_dir()) + + match extension: + "png": + generate_image_from_tags(upscale_amount).save_png(file_path) + _: + # SVG / fallback. + GlobalSettings.modify_save_data("current_file_path", file_path) + save_svg_to_file(file_path) + HandlerGUI.remove_overlay() + func save_svg_to_file(path: String) -> void: var FA := FileAccess.open(path, FileAccess.WRITE) diff --git a/src/ThemeGenerator.gd b/src/ThemeGenerator.gd index 96c4673c..12c0460c 100644 --- a/src/ThemeGenerator.gd +++ b/src/ThemeGenerator.gd @@ -76,7 +76,7 @@ static func generate_theme() -> void: setup_button(theme) setup_checkbox(theme) setup_checkbutton(theme) - setup_filedialog(theme) + setup_itemlist(theme) setup_lineedit(theme) setup_scrollbar(theme) setup_separator(theme) @@ -132,7 +132,7 @@ static func setup_panelcontainer(theme: Theme) -> void: overlay_stylebox.set_border_width_all(2) overlay_stylebox.content_margin_left = 8.0 overlay_stylebox.content_margin_right = 10.0 - overlay_stylebox.content_margin_top = 8.0 + overlay_stylebox.content_margin_top = 6.0 overlay_stylebox.content_margin_bottom = 10.0 overlay_stylebox.bg_color = overlay_panel_inner_color overlay_stylebox.border_color = overlay_panel_border_color @@ -511,11 +511,34 @@ static func setup_checkbutton(theme: Theme) -> void: theme.set_icon("checked", "CheckButton", icon("GuiToggleChecked")) theme.set_icon("unchecked", "CheckButton", icon("GuiToggleUnchecked")) -static func setup_filedialog(theme: Theme) -> void: - theme.add_type("FileDialog") - theme.set_icon("parent_folder", "CheckButton", icon("FolderUp")) - theme.set_icon("reload", "CheckButton", icon("../Reload")) - theme.set_icon("toggle_hidden", "CheckButton", icon("../Visuals")) +static func setup_itemlist(theme: Theme) -> void: + theme.add_type("ItemList") + theme.set_color("font_color", "ItemList", Color(0.9, 0.9, 0.9)) + theme.set_color("font_hovered", "ItemList", Color.WHITE) + theme.set_color("font_selected", "ItemList", Color.WHITE) + theme.set_color("guide_color", "ItemList", Color.TRANSPARENT) + #theme.set_constant("v_separation", "ItemList", 0) + theme.set_constant("icon_margin", "ItemList", 4) + + var empty_stylebox := StyleBoxEmpty.new() + empty_stylebox.set_content_margin_all(1) + theme.set_stylebox("panel", "ItemList", empty_stylebox) + theme.set_stylebox("focus", "ItemList", empty_stylebox) + theme.set_stylebox("cursor", "ItemList", empty_stylebox) + theme.set_stylebox("cursor_unfocused", "ItemList", empty_stylebox) + + var item_stylebox := StyleBoxFlat.new() + item_stylebox.set_corner_radius_all(3) + item_stylebox.set_content_margin_all(2) + + var hover_item_stylebox := item_stylebox.duplicate() + hover_item_stylebox.bg_color = flat_button_color_hover + theme.set_stylebox("hovered", "ItemList", hover_item_stylebox) + + var selected_item_stylebox := item_stylebox.duplicate() + selected_item_stylebox.bg_color = flat_button_color_pressed + theme.set_stylebox("selected", "ItemList", selected_item_stylebox) + theme.set_stylebox("selected_focus", "ItemList", selected_item_stylebox) static func setup_lineedit(theme: Theme) -> void: theme.add_type("LineEdit") diff --git a/src/data_classes/SaveData.gd b/src/data_classes/SaveData.gd index ae45b03c..3f437ab5 100644 --- a/src/data_classes/SaveData.gd +++ b/src/data_classes/SaveData.gd @@ -10,6 +10,7 @@ const GoodColorPicker = preload("res://src/ui_elements/good_color_picker.gd") @export var color_picker_slider_mode := GoodColorPicker.SliderMode.RGB @export var path_command_relative := false @export var last_used_dir := "" +@export var file_dialog_show_hidden := false signal current_file_path_changed @export var current_file_path := "": diff --git a/src/ui_parts/alert_dialog.tscn b/src/ui_parts/alert_dialog.tscn index 02b6d04e..9e08d5f0 100644 --- a/src/ui_parts/alert_dialog.tscn +++ b/src/ui_parts/alert_dialog.tscn @@ -15,40 +15,34 @@ offset_right = 2.0 offset_bottom = 2.0 grow_horizontal = 2 grow_vertical = 2 +theme_type_variation = &"OverlayPanel" script = ExtResource("1_qntyo") -[node name="MarginContainer" type="MarginContainer" parent="."] -layout_mode = 2 -theme_override_constants/margin_left = 8 -theme_override_constants/margin_top = 6 -theme_override_constants/margin_right = 8 -theme_override_constants/margin_bottom = 8 - -[node name="MainContainer" type="VBoxContainer" parent="MarginContainer"] +[node name="MainContainer" type="VBoxContainer" parent="."] unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 10 -[node name="TextContainer" type="VBoxContainer" parent="MarginContainer/MainContainer"] +[node name="TextContainer" type="VBoxContainer" parent="MainContainer"] layout_mode = 2 -[node name="Title" type="Label" parent="MarginContainer/MainContainer/TextContainer"] +[node name="Title" type="Label" parent="MainContainer/TextContainer"] layout_mode = 2 theme_override_fonts/font = ExtResource("1_3yrpq") theme_override_font_sizes/font_size = 16 text = "Alert!" horizontal_alignment = 1 -[node name="Label" type="RichTextLabel" parent="MarginContainer/MainContainer/TextContainer"] +[node name="Label" type="RichTextLabel" parent="MainContainer/TextContainer"] custom_minimum_size = Vector2(180, 0) layout_mode = 2 theme_override_font_sizes/normal_font_size = 12 fit_content = true -[node name="OKButton" type="Button" parent="MarginContainer/MainContainer"] +[node name="OKButton" type="Button" parent="MainContainer"] layout_mode = 2 size_flags_horizontal = 4 mouse_default_cursor_shape = 2 text = "OK" -[connection signal="pressed" from="MarginContainer/MainContainer/OKButton" to="." method="_on_ok_button_pressed"] +[connection signal="pressed" from="MainContainer/OKButton" to="." method="_on_ok_button_pressed"] diff --git a/src/ui_parts/export_dialog.gd b/src/ui_parts/export_dialog.gd index 83263fdc..90f56652 100644 --- a/src/ui_parts/export_dialog.gd +++ b/src/ui_parts/export_dialog.gd @@ -1,7 +1,6 @@ extends PanelContainer const NumberEditType = preload("res://src/ui_elements/number_edit.gd") -const SVGFileDialog = preload("res://src/ui_parts/svg_file_dialog.tscn") var upscale_amount := -1.0 var extension := "" @@ -52,32 +51,17 @@ func _on_dropdown_value_changed(new_value: String) -> void: func native_file_export(has_selected: bool, files: PackedStringArray, _filter_idx: int) -> void: if has_selected: - export(files[0]) + SVG.finish_export(files[0], extension, upscale_amount) func _on_ok_button_pressed() -> void: if OS.has_feature("web"): match extension: "png": - HandlerGUI.web_save_png(create_image()) + HandlerGUI.web_save_png(SVG.generate_image_from_tags()) _: HandlerGUI.web_save_svg() else: - SVG.open_save_dialog(extension, native_file_export, export) - -func export(path: String) -> void: - if path.get_extension().is_empty(): - path += "." + extension - - GlobalSettings.modify_save_data("last_used_dir", path.get_base_dir()) - - match extension: - "png": - create_image().save_png(path) - _: - # SVG / fallback. - GlobalSettings.modify_save_data("current_file_path", path) - SVG.save_svg_to_file(path) - HandlerGUI.remove_overlay() + SVG.open_save_dialog(extension, native_file_export, SVG.finish_export.bind(extension)) func _on_cancel_button_pressed() -> void: HandlerGUI.remove_overlay() @@ -94,14 +78,3 @@ func update_final_scale() -> void: func update_extension_configuration() -> void: scale_container.visible = (extension == "png") - -func create_image() -> Image: - var export_svg := SVG.root_tag.create_duplicate() - if export_svg.attributes.viewBox.get_list().is_empty(): - export_svg.attributes.viewBox.set_list([0, 0, export_svg.width, export_svg.height]) - export_svg.attributes.width.set_num(export_svg.width * upscale_amount) - export_svg.attributes.height.set_num(export_svg.height * upscale_amount) - var img := Image.new() - img.load_svg_from_string(SVGParser.svg_to_text(export_svg)) - img.fix_alpha_edges() # See godot issue 82579. - return img diff --git a/src/ui_parts/export_dialog.tscn b/src/ui_parts/export_dialog.tscn index dde213df..96ed80db 100644 --- a/src/ui_parts/export_dialog.tscn +++ b/src/ui_parts/export_dialog.tscn @@ -108,17 +108,18 @@ theme_override_font_sizes/font_size = 10 [node name="ButtonContainer" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 -theme_override_constants/separation = 12 alignment = 1 [node name="CancelButton" type="Button" parent="VBoxContainer/ButtonContainer"] layout_mode = 2 +size_flags_horizontal = 6 focus_mode = 0 mouse_default_cursor_shape = 2 text = "Cancel" [node name="OKButton" type="Button" parent="VBoxContainer/ButtonContainer"] layout_mode = 2 +size_flags_horizontal = 6 focus_mode = 0 mouse_default_cursor_shape = 2 text = "Export" diff --git a/src/ui_parts/good_file_dialog.gd b/src/ui_parts/good_file_dialog.gd new file mode 100644 index 00000000..b03faf0f --- /dev/null +++ b/src/ui_parts/good_file_dialog.gd @@ -0,0 +1,289 @@ +## A fallback file dialog for if the native file dialog is not available. +extends PanelContainer + +const AlertDialog = preload("res://src/ui_parts/alert_dialog.tscn") + +signal file_selected(path: String) + +const folder_icon = preload("res://visual/icons/Folder.svg") +const system_dir_icons = { + OS.SYSTEM_DIR_DESKTOP: preload("res://visual/icons/DirDesktop.svg"), + OS.SYSTEM_DIR_DOCUMENTS: preload("res://visual/icons/DirDocuments.svg"), + OS.SYSTEM_DIR_DOWNLOADS: preload("res://visual/icons/DirDownloads.svg"), + OS.SYSTEM_DIR_MOVIES: preload("res://visual/icons/DirMovies.svg"), + OS.SYSTEM_DIR_MUSIC: preload("res://visual/icons/DirMusic.svg"), + OS.SYSTEM_DIR_PICTURES: preload("res://visual/icons/DirPictures.svg"), +} + +var item_height := 16 + +enum FileMode {SELECT, SAVE} +var mode: FileMode + +var current_dir: String +var current_file := "" +var default_file := "" +var extension := "" + +var search_text := "" + +@onready var close_button: Button = %CloseButton +@onready var special_button: Button = %SpecialButton +@onready var path_label: Label = %PathLabel +@onready var title_label: Label = $VBoxContainer/TitleLabel +@onready var search_field: BetterLineEdit = %SearchField +@onready var path_field: BetterLineEdit = %PathField +@onready var file_container: HBoxContainer = %FileContainer +@onready var file_field: BetterLineEdit = %FileField +@onready var extension_panel: PanelContainer = %ExtensionPanel +@onready var extension_label: Label = %ExtensionLabel +@onready var system_dir_list: ItemList = %SystemDirList +@onready var file_list: ItemList = %FileList +@onready var folder_up_button: Button = %FolderUpButton +@onready var refresh_button: Button = %RefreshButton +@onready var show_hidden_button: Button = %ShowHiddenButton +@onready var search_button: Button = %SearchButton + +@onready var center_container: CenterContainer = $CenterContainer +@onready var alert_title_label: Label = %AlertTitleLabel +@onready var alert_label: RichTextLabel = %AlertLabel +@onready var alert_cancel_button: Button = %AlertCancelButton +@onready var alert_replace_button: Button = %AlertReplaceButton + + +class Actions: + var activation_action: Callable + var selection_action: Callable + + func _init(on_activation := Callable(), on_selection := Callable()) -> void: + activation_action = on_activation + selection_action = on_selection + +func call_activation_action(actions: Actions) -> void: + if actions != null and not actions.activation_action.is_null(): + actions.activation_action.call() + +func call_selection_action(actions: Actions) -> void: + if actions != null and not actions.selection_action.is_null(): + actions.selection_action.call() + + +func setup(new_dir: String, new_file: String, new_mode: FileMode, +new_extension: String) -> void: + current_dir = new_dir + current_file = new_file + if new_mode == FileMode.SAVE: + default_file = new_file + mode = new_mode + extension = new_extension + +func _ready() -> void: + if mode == FileMode.SELECT: + file_container.hide() + if mode == FileMode.SAVE: + alert_title_label.text = tr("Alert!") + alert_cancel_button.text = tr("Cancel") + alert_replace_button.text = tr("Replace") + var extension_panel_stylebox := extension_panel.get_theme_stylebox("panel") + extension_panel_stylebox.content_margin_top -= 4 + extension_panel.add_theme_stylebox_override("panel", extension_panel_stylebox) + if GlobalSettings.save_data.file_dialog_show_hidden: + show_hidden_button.set_pressed_no_signal(true) + folder_up_button.tooltip_text = tr("Go to parent folder") + refresh_button.tooltip_text = tr("Refresh files") + show_hidden_button.tooltip_text = tr("Toggle the visibility of hidden files") + search_button.tooltip_text = tr("Search files") + + title_label.text = tr("Select an SVG") if mode == FileMode.SELECT else tr("Save SVG") + close_button.text = tr("Close") + special_button.text = tr("Select") if mode == FileMode.SELECT else tr("Save") + path_label.text = tr("Path") + ":" + extension_label.text = "." + extension + for dir in [OS.SYSTEM_DIR_DESKTOP, OS.SYSTEM_DIR_DOCUMENTS, OS.SYSTEM_DIR_DOWNLOADS, + OS.SYSTEM_DIR_MOVIES, OS.SYSTEM_DIR_MUSIC, OS.SYSTEM_DIR_PICTURES]: + var dir_string := OS.get_system_dir(dir) + if dir_string.is_empty(): + continue + + var item_idx := system_dir_list.add_item(dir_string.get_file(), + system_dir_icons[dir] if dir in system_dir_icons else folder_icon) + system_dir_list.set_item_metadata(item_idx, + Actions.new(Callable(), enter_dir.bind(dir_string))) + # If you start off in a system dir, highlight it in the list to make it clear. + if current_dir == dir_string: + system_dir_list.select(item_idx) + + # Should always be safe. + set_dir(current_dir) + if mode == FileMode.SAVE: + set_file(current_file) + file_field.grab_focus() + else: + special_button.disabled = true + special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW + + +func enter_dir(dir: String) -> void: + if search_button.button_pressed: + search_button.button_pressed = false + set_dir(dir) + +func file_sort(file1: String, file2: String) -> bool: + return file1.naturalnocasecmp_to(file2) == -1 + +# This function requires a safe input. +func set_dir(dir: String) -> void: + file_list.clear() + # Basic setup. + current_dir = dir + path_field.text = current_dir + # Create the DirAccess object. + var DA := DirAccess.open(dir) + DA.include_hidden = GlobalSettings.save_data.file_dialog_show_hidden + # Gather the files and directories. + var directories: Array[String] = [] + var files: Array[String] = [] + for directory in DA.get_directories(): + directories.append(directory) + for file in DA.get_files(): + files.append(file) + directories.sort_custom(file_sort) + files.sort_custom(file_sort) + # Populate the ItemList. + for directory in directories: + if not search_text.is_empty() and not search_text.is_subsequence_ofn(directory): + continue + var item_idx := file_list.add_item(directory, folder_icon) + file_list.set_item_metadata(item_idx, + Actions.new(enter_dir.bind(current_dir.path_join(directory)), unfocus_file)) + + for file in files: + if file.get_extension() != extension or\ + (not search_text.is_empty() and not search_text.is_subsequence_ofn(file)): + continue + + match extension: + "svg": + # Setup a clean SVG graphic by using the scaling parameter. + var svg_text := FileAccess.open(current_dir.path_join(file), + FileAccess.READ).get_as_text() + var img := Image.new() + img.load_svg_from_string(svg_text) + img.load_svg_from_string(svg_text, + item_height / maxf(img.get_width(), img.get_height())) + var item_idx := file_list.add_item(file, ImageTexture.create_from_image(img)) + file_list.set_item_metadata(item_idx, + Actions.new(select_file, focus_file.bind(file))) + "png": + var item_idx := file_list.add_item(file, ImageTexture.create_from_image( + Image.load_from_file(current_dir.path_join(file)))) + file_list.set_item_metadata(item_idx, + Actions.new(select_file, focus_file.bind(file))) + file = DA.get_next() + +func set_file(file: String) -> void: + if mode == FileMode.SELECT: + if file.is_empty(): + special_button.disabled = true + special_button.mouse_default_cursor_shape = Control.CURSOR_ARROW + else: + special_button.disabled = false + special_button.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + if not file.is_empty(): + if file.get_extension() != extension: + file += "." + extension + current_file = file + file_field.text = current_file + + +func select_file() -> void: + if mode == FileMode.SAVE and current_file in DirAccess.get_files_at(current_dir): + alert_label.text = tr("A file named \"{file_name}\" already exists. Replacing will overwrite its contents!").format({"file_name": current_file}) + center_container.show() + alert_replace_button.grab_focus() + else: + file_selected.emit(current_dir.path_join(current_file)) + +func focus_file(path: String) -> void: + set_file(path.get_file()) + +func unfocus_file() -> void: + set_file(default_file) + +func _on_folder_up_button_pressed() -> void: + set_dir(current_dir.get_base_dir()) + +func _on_file_list_empty_clicked(_at_position: Vector2, _mouse_button_index: int) -> void: + file_list.deselect_all() + unfocus_file() + +func _on_system_dir_list_empty_clicked(_at_position: Vector2, +_mouse_button_index: int) -> void: + system_dir_list.deselect_all() + +func _on_file_list_item_activated(index: int) -> void: + call_activation_action(file_list.get_item_metadata(index)) + +func _on_file_list_item_selected(index: int) -> void: + call_selection_action(file_list.get_item_metadata(index)) + +func _on_system_dir_list_item_selected(index: int) -> void: + call_selection_action(system_dir_list.get_item_metadata(index)) + +func _on_refresh_button_pressed() -> void: + set_dir(current_dir) + +func _on_show_hidden_button_toggled(toggled_on: bool) -> void: + GlobalSettings.modify_save_data("file_dialog_show_hidden", toggled_on) + set_dir(current_dir) + +func _on_search_button_toggled(toggled_on: bool) -> void: + if toggled_on: + search_field.show() + search_field.grab_focus() + else: + search_field.hide() + search_text = "" + set_dir(current_dir) + + +func _on_close_button_pressed() -> void: + queue_free() + +func _on_file_selected(_path: String) -> void: + queue_free() + +func _on_special_button_pressed() -> void: + select_file() + +func _on_file_field_text_submitted(new_text: String) -> void: + file_field.remove_theme_color_override("font_color") + if new_text.is_valid_filename(): + current_file = new_text + else: + file_field.text = current_file + +func _on_path_field_text_submitted(new_text: String) -> void: + var DA := DirAccess.open(new_text) + if DA != null: + set_dir(new_text) + else: + path_field.text = current_dir + +func _on_search_field_text_changed(new_text: String) -> void: + search_text = new_text + set_dir(current_dir) + +func _on_search_field_text_change_canceled() -> void: + search_field.text = search_text + +func _on_file_field_text_changed(new_text: String) -> void: + file_field.add_theme_color_override("font_color", + GlobalSettings.get_validity_color(!new_text.is_valid_filename())) + + +func _on_alert_cancel_button_pressed() -> void: + center_container.hide() + +func _on_alert_replace_button_pressed() -> void: + file_selected.emit(current_dir.path_join(current_file)) diff --git a/src/ui_parts/good_file_dialog.tscn b/src/ui_parts/good_file_dialog.tscn new file mode 100644 index 00000000..840f2464 --- /dev/null +++ b/src/ui_parts/good_file_dialog.tscn @@ -0,0 +1,205 @@ +[gd_scene load_steps=8 format=3 uid="uid://bndmdmjlwqxfh"] + +[ext_resource type="Script" path="res://src/ui_parts/good_file_dialog.gd" id="1_awdto"] +[ext_resource type="Script" path="res://src/ui_elements/BetterLineEdit.gd" id="2_52puw"] +[ext_resource type="Texture2D" uid="uid://rrhdja8l17cn" path="res://visual/icons/FolderUp.svg" id="2_jjo15"] +[ext_resource type="Texture2D" uid="uid://cvh3kwbucf2n1" path="res://visual/icons/Reload.svg" id="4_udwbh"] +[ext_resource type="Texture2D" uid="uid://kkxyv1gyrjgj" path="res://visual/icons/Visuals.svg" id="5_2ggtv"] +[ext_resource type="Texture2D" uid="uid://d4c7haflm8evm" path="res://visual/icons/Search.svg" id="6_otods"] +[ext_resource type="FontFile" uid="uid://dc0w4sx0h0fui" path="res://visual/fonts/FontBold.ttf" id="7_br1c0"] + +[node name="NonNativeFileDialog" type="PanelContainer"] +offset_right = 684.0 +offset_bottom = 386.0 +theme_type_variation = &"OverlayPanel" +script = ExtResource("1_awdto") + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="TitleLabel" type="Label" parent="VBoxContainer"] +layout_mode = 2 +horizontal_alignment = 1 + +[node name="TopBar" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 6 + +[node name="FolderUpButton" type="Button" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"IconButton" +icon = ExtResource("2_jjo15") + +[node name="PathLabel" type="Label" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="PathField" type="LineEdit" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("2_52puw") + +[node name="RefreshButton" type="Button" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"IconButton" +icon = ExtResource("4_udwbh") + +[node name="ShowHiddenButton" type="Button" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"IconButton" +toggle_mode = true +icon = ExtResource("5_2ggtv") + +[node name="SearchButton" type="Button" parent="VBoxContainer/TopBar"] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +theme_type_variation = &"IconButton" +toggle_mode = true +icon = ExtResource("6_otods") + +[node name="MainContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 + +[node name="SystemDirContainer" type="PanelContainer" parent="VBoxContainer/MainContainer"] +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 + +[node name="SystemDirList" type="ItemList" parent="VBoxContainer/MainContainer/SystemDirContainer"] +unique_name_in_owner = true +layout_mode = 2 +fixed_icon_size = Vector2i(16, 16) + +[node name="FilePicker" type="VBoxContainer" parent="VBoxContainer/MainContainer"] +custom_minimum_size = Vector2(0, 300) +layout_mode = 2 + +[node name="SearchField" type="LineEdit" parent="VBoxContainer/MainContainer/FilePicker"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +script = ExtResource("2_52puw") + +[node name="VisualPicker" type="PanelContainer" parent="VBoxContainer/MainContainer/FilePicker"] +custom_minimum_size = Vector2(450, 0) +layout_mode = 2 +size_flags_vertical = 3 + +[node name="FileList" type="ItemList" parent="VBoxContainer/MainContainer/FilePicker/VisualPicker"] +unique_name_in_owner = true +layout_mode = 2 +fixed_icon_size = Vector2i(16, 16) + +[node name="FileContainer" type="HBoxContainer" parent="VBoxContainer/MainContainer/FilePicker"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="FileField" type="LineEdit" parent="VBoxContainer/MainContainer/FilePicker/FileContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +script = ExtResource("2_52puw") + +[node name="ExtensionPanel" type="PanelContainer" parent="VBoxContainer/MainContainer/FilePicker/FileContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"DarkPanel" + +[node name="ExtensionLabel" type="Label" parent="VBoxContainer/MainContainer/FilePicker/FileContainer/ExtensionPanel"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0.866667, 0.933333, 1, 0.533333) + +[node name="ButtonsContainer" type="HBoxContainer" parent="VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 12 +alignment = 1 + +[node name="CloseButton" type="Button" parent="VBoxContainer/ButtonsContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 6 +focus_mode = 0 +mouse_default_cursor_shape = 2 + +[node name="SpecialButton" type="Button" parent="VBoxContainer/ButtonsContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 6 +focus_mode = 0 +mouse_default_cursor_shape = 2 + +[node name="CenterContainer" type="CenterContainer" parent="."] +visible = false +layout_mode = 2 + +[node name="AlertDialog" type="PanelContainer" parent="CenterContainer"] +custom_minimum_size = Vector2(280, 0) +layout_mode = 2 +theme_type_variation = &"OverlayPanel" + +[node name="MainContainer" type="VBoxContainer" parent="CenterContainer/AlertDialog"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_constants/separation = 10 + +[node name="TextContainer" type="VBoxContainer" parent="CenterContainer/AlertDialog/MainContainer"] +layout_mode = 2 + +[node name="AlertTitleLabel" type="Label" parent="CenterContainer/AlertDialog/MainContainer/TextContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_fonts/font = ExtResource("7_br1c0") +theme_override_font_sizes/font_size = 16 +horizontal_alignment = 1 + +[node name="AlertLabel" type="RichTextLabel" parent="CenterContainer/AlertDialog/MainContainer/TextContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(180, 0) +layout_mode = 2 +theme_override_font_sizes/normal_font_size = 12 +fit_content = true + +[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/AlertDialog/MainContainer"] +layout_mode = 2 +alignment = 1 + +[node name="AlertCancelButton" type="Button" parent="CenterContainer/AlertDialog/MainContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 6 +mouse_default_cursor_shape = 2 + +[node name="AlertReplaceButton" type="Button" parent="CenterContainer/AlertDialog/MainContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 6 +mouse_default_cursor_shape = 2 + +[connection signal="file_selected" from="." to="." method="_on_file_selected"] +[connection signal="pressed" from="VBoxContainer/TopBar/FolderUpButton" to="." method="_on_folder_up_button_pressed"] +[connection signal="text_submitted" from="VBoxContainer/TopBar/PathField" to="." method="_on_path_field_text_submitted"] +[connection signal="pressed" from="VBoxContainer/TopBar/RefreshButton" to="." method="_on_refresh_button_pressed"] +[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="empty_clicked" from="VBoxContainer/MainContainer/SystemDirContainer/SystemDirList" to="." method="_on_system_dir_list_empty_clicked"] +[connection signal="item_selected" from="VBoxContainer/MainContainer/SystemDirContainer/SystemDirList" to="." method="_on_system_dir_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_selected" from="VBoxContainer/MainContainer/FilePicker/VisualPicker/FileList" to="." method="_on_file_list_item_selected"] +[connection signal="text_changed" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_changed"] +[connection signal="text_submitted" from="VBoxContainer/MainContainer/FilePicker/FileContainer/FileField" to="." method="_on_file_field_text_submitted"] +[connection signal="pressed" from="VBoxContainer/ButtonsContainer/CloseButton" to="." method="_on_close_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ButtonsContainer/SpecialButton" to="." method="_on_special_button_pressed"] +[connection signal="pressed" from="CenterContainer/AlertDialog/MainContainer/HBoxContainer/AlertCancelButton" to="." method="_on_alert_cancel_button_pressed"] +[connection signal="pressed" from="CenterContainer/AlertDialog/MainContainer/HBoxContainer/AlertReplaceButton" to="." method="_on_alert_replace_button_pressed"] diff --git a/src/ui_parts/import_warning_dialog.tscn b/src/ui_parts/import_warning_dialog.tscn index 8c6a10be..f8a69754 100644 --- a/src/ui_parts/import_warning_dialog.tscn +++ b/src/ui_parts/import_warning_dialog.tscn @@ -16,33 +16,27 @@ offset_right = 206.0 offset_bottom = 111.0 grow_horizontal = 2 grow_vertical = 2 +theme_type_variation = &"OverlayPanel" script = ExtResource("1_1rv5w") -[node name="MarginContainer" type="MarginContainer" parent="."] -layout_mode = 2 -theme_override_constants/margin_left = 8 -theme_override_constants/margin_top = 8 -theme_override_constants/margin_right = 8 -theme_override_constants/margin_bottom = 8 - -[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +[node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 theme_override_constants/separation = 12 -[node name="Title" type="Label" parent="MarginContainer/VBoxContainer"] +[node name="Title" type="Label" parent="VBoxContainer"] layout_mode = 2 text = "Import Problems" horizontal_alignment = 1 -[node name="TextureContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +[node name="TextureContainer" type="HBoxContainer" parent="VBoxContainer"] layout_mode = 2 theme_override_constants/separation = 12 -[node name="TexturePreview" parent="MarginContainer/VBoxContainer/TextureContainer" instance=ExtResource("2_j1v8v")] +[node name="TexturePreview" parent="VBoxContainer/TextureContainer" instance=ExtResource("2_j1v8v")] unique_name_in_owner = true layout_mode = 2 -[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/TextureContainer"] +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/TextureContainer"] unique_name_in_owner = true custom_minimum_size = Vector2(256, 128) layout_mode = 2 @@ -53,10 +47,10 @@ theme_override_constants/margin_top = 6 theme_override_constants/margin_right = 4 theme_override_constants/margin_bottom = 6 -[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/TextureContainer/MarginContainer"] +[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/TextureContainer/MarginContainer"] layout_mode = 2 -[node name="WarningsLabel" type="RichTextLabel" parent="MarginContainer/VBoxContainer/TextureContainer/MarginContainer/ScrollContainer"] +[node name="WarningsLabel" type="RichTextLabel" parent="VBoxContainer/TextureContainer/MarginContainer/ScrollContainer"] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 @@ -67,22 +61,24 @@ fit_content = true scroll_active = false autowrap_mode = 0 -[node name="ButtonContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +[node name="ButtonContainer" type="HBoxContainer" parent="VBoxContainer"] unique_name_in_owner = true layout_mode = 2 theme_override_constants/separation = 12 alignment = 1 -[node name="CancelButton" type="Button" parent="MarginContainer/VBoxContainer/ButtonContainer"] +[node name="CancelButton" type="Button" parent="VBoxContainer/ButtonContainer"] layout_mode = 2 +size_flags_horizontal = 6 mouse_default_cursor_shape = 2 text = "Cancel" -[node name="OKButton" type="Button" parent="MarginContainer/VBoxContainer/ButtonContainer"] +[node name="OKButton" type="Button" parent="VBoxContainer/ButtonContainer"] layout_mode = 2 +size_flags_horizontal = 6 mouse_default_cursor_shape = 2 text = "Import" [connection signal="imported" from="." to="." method="_on_imported"] -[connection signal="pressed" from="MarginContainer/VBoxContainer/ButtonContainer/CancelButton" to="." method="_on_cancel_button_pressed"] -[connection signal="pressed" from="MarginContainer/VBoxContainer/ButtonContainer/OKButton" to="." method="_on_ok_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ButtonContainer/CancelButton" to="." method="_on_cancel_button_pressed"] +[connection signal="pressed" from="VBoxContainer/ButtonContainer/OKButton" to="." method="_on_ok_button_pressed"] diff --git a/src/ui_parts/settings_menu.gd b/src/ui_parts/settings_menu.gd index 4a1abdce..9a9e36ff 100644 --- a/src/ui_parts/settings_menu.gd +++ b/src/ui_parts/settings_menu.gd @@ -14,7 +14,10 @@ const SettingColor = preload("res://src/ui_elements/setting_color.gd") @onready var shortcut_container: VBoxContainer = %ShortcutContainer @onready var content_container: MarginContainer = %ContentContainer @onready var tabs: VBoxContainer = %Tabs + @onready var wrap_mouse: HBoxContainer = %WrapMouse +@onready var use_native_file_dialog: HBoxContainer = %UseNativeFileDialog + @onready var configurable_shortcuts: VBoxContainer = %ConfigurableShortcuts @onready var non_configurable_shortcuts: VBoxContainer = %NonConfigurableShortcuts @@ -61,14 +64,17 @@ func setup_setting_labels() -> void: tabs.get_node(^"ThemeTab").text = tr("Theme") tabs.get_node(^"OtherTab").text = tr("Other") - var invert_zoom := %ContentContainer/Other/Input/InvertZoom + var invert_zoom := %ContentContainer/Other/OtherSettings/Input/InvertZoom invert_zoom.label.text = tr("Invert zoom direction") invert_zoom.label.tooltip_text = tr("Swaps zoom in and zoom out with the mouse wheel.") wrap_mouse.label.text = tr("Wrap mouse") wrap_mouse.label.tooltip_text = tr("Wraps the mouse cursor around when panning the viewport.") - var ctrl_for_zoom := %ContentContainer/Other/Input/UseCtrlForZoom + var ctrl_for_zoom := %ContentContainer/Other/OtherSettings/Input/UseCtrlForZoom ctrl_for_zoom.label.text = tr("Use CTRL for zooming") ctrl_for_zoom.label.tooltip_text = tr("If turned on, scrolling will pan the view. To zoom, hold CTRL while scrolling.") + use_native_file_dialog.label.text = tr("Use native file dialog") + use_native_file_dialog.label.tooltip_text = tr("If turned on, uses your operating system's native file dialog. If turned off, uses GodSVG's built-in file dialog.") + %GeneralVBox/NumberPrecision.label.text = tr("Number precision digits") %GeneralVBox/AnglePrecision.label.text = tr("Angle precision digits") %XMLVBox/AddTrailingNewline.label.text = tr("Add trailing newline") @@ -284,7 +290,15 @@ func _on_theme_tab_toggled(toggled_on: bool) -> void: func _on_other_tab_toggled(toggled_on: bool) -> void: if toggled_on and not generated_content.other: + # Disable mouse wrap if not available. if not DisplayServer.has_feature(DisplayServer.FEATURE_MOUSE_WARP): wrap_mouse.checkbox.set_pressed_no_signal(false) wrap_mouse.set_checkbox_enabled(false) + # Disable fallback file dialog on web, and native file dialog if not available. + if OS.has_feature("web"): + use_native_file_dialog.checkbox.set_pressed_no_signal(true) + use_native_file_dialog.set_checkbox_enabled(false) + if not DisplayServer.has_feature(DisplayServer.FEATURE_NATIVE_DIALOG): + use_native_file_dialog.checkbox.set_pressed_no_signal(false) + use_native_file_dialog.set_checkbox_enabled(false) generated_content.other = true diff --git a/src/ui_parts/settings_menu.tscn b/src/ui_parts/settings_menu.tscn index 34b21147..72d0b11f 100644 --- a/src/ui_parts/settings_menu.tscn +++ b/src/ui_parts/settings_menu.tscn @@ -480,35 +480,57 @@ visible = false layout_mode = 2 horizontal_scroll_mode = 0 -[node name="Input" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other"] +[node name="OtherSettings" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/separation = 8 + +[node name="Input" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings"] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 theme_override_constants/separation = 3 -[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/Input"] +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/Input"] layout_mode = 2 text = "Input" -[node name="InvertZoom" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/Input" instance=ExtResource("4_2qeh2")] +[node name="InvertZoom" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/Input" instance=ExtResource("4_2qeh2")] layout_mode = 2 tooltip_text = "Swaps zoom in and zoom out with the mouse wheel." section_name = "other" setting_name = "invert_zoom" -[node name="WrapMouse" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/Input" instance=ExtResource("4_2qeh2")] +[node name="WrapMouse" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/Input" instance=ExtResource("4_2qeh2")] unique_name_in_owner = true layout_mode = 2 tooltip_text = "Wraps the mouse cursor around when panning the viewport." section_name = "other" setting_name = "wrap_mouse" -[node name="UseCtrlForZoom" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/Input" instance=ExtResource("4_2qeh2")] +[node name="UseCtrlForZoom" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/Input" instance=ExtResource("4_2qeh2")] layout_mode = 2 tooltip_text = "If turned on, scrolling will pan the view. To zoom, hold CTRL while scrolling." section_name = "other" setting_name = "use_ctrl_for_zoom" +[node name="FileDialog" type="VBoxContainer" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 3 + +[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/FileDialog"] +layout_mode = 2 +text = "Miscellaneous" + +[node name="UseNativeFileDialog" parent="VBoxContainer/HBoxContainer/MainPanel/ContentContainer/Other/OtherSettings/FileDialog" instance=ExtResource("4_2qeh2")] +unique_name_in_owner = true +layout_mode = 2 +tooltip_text = "Swaps zoom in and zoom out with the mouse wheel." +section_name = "other" +setting_name = "use_native_file_dialog" + [node name="Close" type="Button" parent="VBoxContainer"] layout_mode = 2 size_flags_horizontal = 4 diff --git a/src/ui_parts/svg_file_dialog.gd b/src/ui_parts/svg_file_dialog.gd deleted file mode 100644 index 311ce27c..00000000 --- a/src/ui_parts/svg_file_dialog.gd +++ /dev/null @@ -1,10 +0,0 @@ -extends FileDialog - -func _on_file_selected(_path: String) -> void: - queue_free() - -func _on_canceled() -> void: - queue_free() - -func _on_confirmed() -> void: - queue_free() diff --git a/src/ui_parts/svg_file_dialog.tscn b/src/ui_parts/svg_file_dialog.tscn deleted file mode 100644 index 31457a00..00000000 --- a/src/ui_parts/svg_file_dialog.tscn +++ /dev/null @@ -1,19 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://bndmdmjlwqxfh"] - -[ext_resource type="Script" path="res://src/ui_parts/svg_file_dialog.gd" id="1_nw1bg"] - -[node name="SVGFileDialog" type="FileDialog"] -title = "Open a File" -position = Vector2i(160, 135) -size = Vector2i(700, 400) -visible = true -ok_button_text = "Open" -cancel_button_text = "Cancel" -file_mode = 0 -access = 2 -filters = PackedStringArray("*.svg ; Scalable vector graphics") -script = ExtResource("1_nw1bg") - -[connection signal="canceled" from="." to="." method="_on_canceled"] -[connection signal="confirmed" from="." to="." method="_on_confirmed"] -[connection signal="file_selected" from="." to="." method="_on_file_selected"] diff --git a/translations/GodSVG.pot b/translations/GodSVG.pot index d03296e5..9c1460a4 100644 --- a/translations/GodSVG.pot +++ b/translations/GodSVG.pot @@ -325,6 +325,36 @@ msgstr "" msgid "Disable the color" msgstr "" +msgid "Path" +msgstr "" + +msgid "Replace" +msgstr "" + +msgid "Go to parent folder" +msgstr "" + +msgid "Refresh files" +msgstr "" + +msgid "Toggle the visibility of hidden files" +msgstr "" + +msgid "Search files" +msgstr "" + +msgid "Select an SVG" +msgstr "" + +msgid "Save SVG" +msgstr "" + +msgid "Select" +msgstr "" + +msgid "A file named \"{file_name}\" already exists. Replacing will overwrite its contents!" +msgstr "" + msgid "Godot third-party components" msgstr "" diff --git a/translations/bg.po b/translations/bg.po index 45ca1240..5babb104 100644 --- a/translations/bg.po +++ b/translations/bg.po @@ -328,6 +328,38 @@ msgstr "Включи цвета" msgid "Disable the color" msgstr "Изключи цвета" +msgid "Path" +msgstr "Пътека" + +msgid "Replace" +msgstr "Замени" + +msgid "Go to parent folder" +msgstr "Отиди в родителската папка" + +msgid "Refresh files" +msgstr "Обнови файловете" + +msgid "Toggle the visibility of hidden files" +msgstr "Превключи видимостта на скритите файлове" + +msgid "Search files" +msgstr "Търсене из файловете" + +msgid "Select an SVG" +msgstr "Избери SVG" + +msgid "Save SVG" +msgstr "Запиши SVG" + +msgid "Select" +msgstr "Избери" + +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "Файл кръстен \"{file_name}\" вече съществува. Заместването ще пренапише неговото съдържание!" + msgid "Godot third-party components" msgstr "Компоненти в Godot от трети лица" diff --git a/translations/de.po b/translations/de.po index 20dd01ac..9d174828 100644 --- a/translations/de.po +++ b/translations/de.po @@ -339,6 +339,43 @@ msgstr "Farbe aktivieren" msgid "Disable the color" msgstr "Farbe deaktivieren" +#, fuzzy +msgid "Path" +msgstr "Pfade" + +msgid "Replace" +msgstr "" + +msgid "Go to parent folder" +msgstr "" + +msgid "Refresh files" +msgstr "" + +msgid "Toggle the visibility of hidden files" +msgstr "" + +#, fuzzy +msgid "Search files" +msgstr "Farbe suchen" + +#, fuzzy +msgid "Select an SVG" +msgstr "Alle Tags auswählen" + +#, fuzzy +msgid "Save SVG" +msgstr "Speichern" + +#, fuzzy +msgid "Select" +msgstr "Farbe löschen" + +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "" + msgid "Godot third-party components" msgstr "Godot Drittanbieter-Komponente" diff --git a/translations/en.po b/translations/en.po index 5dd32484..f2842b3c 100644 --- a/translations/en.po +++ b/translations/en.po @@ -326,6 +326,38 @@ msgstr "" msgid "Disable the color" msgstr "" +msgid "Path" +msgstr "" + +msgid "Replace" +msgstr "" + +msgid "Go to parent folder" +msgstr "" + +msgid "Refresh files" +msgstr "" + +msgid "Toggle the visibility of hidden files" +msgstr "" + +msgid "Search files" +msgstr "" + +msgid "Select an SVG" +msgstr "" + +msgid "Save SVG" +msgstr "" + +msgid "Select" +msgstr "" + +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "" + msgid "Godot third-party components" msgstr "" diff --git a/translations/ru.po b/translations/ru.po index 56e2b9ef..c3208d68 100644 --- a/translations/ru.po +++ b/translations/ru.po @@ -335,6 +335,43 @@ msgstr "Активировать цвет" msgid "Disable the color" msgstr "Деактивировать цвет" +#, fuzzy +msgid "Path" +msgstr "Пути" + +msgid "Replace" +msgstr "" + +msgid "Go to parent folder" +msgstr "" + +msgid "Refresh files" +msgstr "" + +msgid "Toggle the visibility of hidden files" +msgstr "" + +#, fuzzy +msgid "Search files" +msgstr "Искать цвет" + +#, fuzzy +msgid "Select an SVG" +msgstr "Выбрать все теги" + +#, fuzzy +msgid "Save SVG" +msgstr "Сохранить" + +#, fuzzy +msgid "Select" +msgstr "Цвет выбранного" + +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "" + msgid "Godot third-party components" msgstr "Компоненты Godot от третьих лиц" diff --git a/translations/uk.po b/translations/uk.po index d765c042..8db21718 100644 --- a/translations/uk.po +++ b/translations/uk.po @@ -336,6 +336,43 @@ msgstr "Активувати колір" msgid "Disable the color" msgstr "Деактивувати колір" +#, fuzzy +msgid "Path" +msgstr "Шляхи" + +msgid "Replace" +msgstr "" + +msgid "Go to parent folder" +msgstr "" + +msgid "Refresh files" +msgstr "" + +msgid "Toggle the visibility of hidden files" +msgstr "" + +#, fuzzy +msgid "Search files" +msgstr "Шукати колір" + +#, fuzzy +msgid "Select an SVG" +msgstr "Обрати усі теги" + +#, fuzzy +msgid "Save SVG" +msgstr "Зберегти" + +#, fuzzy +msgid "Select" +msgstr "Колір обраного" + +msgid "" +"A file named \"{file_name}\" already exists. Replacing will overwrite its " +"contents!" +msgstr "" + msgid "Godot third-party components" msgstr "Компоненти Godot від третіх сторін" diff --git a/visual/icon.svg b/visual/icon.svg index 564abd4e..317263a3 100644 --- a/visual/icon.svg +++ b/visual/icon.svg @@ -1 +1 @@ - + \ No newline at end of file diff --git a/visual/icons/DirDesktop.svg b/visual/icons/DirDesktop.svg new file mode 100644 index 00000000..bee51115 --- /dev/null +++ b/visual/icons/DirDesktop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirDesktop.svg.import b/visual/icons/DirDesktop.svg.import new file mode 100644 index 00000000..bf6712a9 --- /dev/null +++ b/visual/icons/DirDesktop.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://7tukdjdfcrwg" +path="res://.godot/imported/DirDesktop.svg-acb7dad19a064fd6b7cbb149a66be88c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirDesktop.svg" +dest_files=["res://.godot/imported/DirDesktop.svg-acb7dad19a064fd6b7cbb149a66be88c.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/DirDocuments.svg b/visual/icons/DirDocuments.svg new file mode 100644 index 00000000..539e47c1 --- /dev/null +++ b/visual/icons/DirDocuments.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirDocuments.svg.import b/visual/icons/DirDocuments.svg.import new file mode 100644 index 00000000..a79b9978 --- /dev/null +++ b/visual/icons/DirDocuments.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bpmks3pbl18rl" +path="res://.godot/imported/DirDocuments.svg-b1ef55a3e2fd0b6809986fa07c707238.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirDocuments.svg" +dest_files=["res://.godot/imported/DirDocuments.svg-b1ef55a3e2fd0b6809986fa07c707238.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/DirDownloads.svg b/visual/icons/DirDownloads.svg new file mode 100644 index 00000000..35b4e588 --- /dev/null +++ b/visual/icons/DirDownloads.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirDownloads.svg.import b/visual/icons/DirDownloads.svg.import new file mode 100644 index 00000000..ea620f90 --- /dev/null +++ b/visual/icons/DirDownloads.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://biq08throgeuw" +path="res://.godot/imported/DirDownloads.svg-749be2df5e3e8e86654497d33aaef37a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirDownloads.svg" +dest_files=["res://.godot/imported/DirDownloads.svg-749be2df5e3e8e86654497d33aaef37a.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/DirMovies.svg b/visual/icons/DirMovies.svg new file mode 100644 index 00000000..0a43fb26 --- /dev/null +++ b/visual/icons/DirMovies.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirMovies.svg.import b/visual/icons/DirMovies.svg.import new file mode 100644 index 00000000..9cf6229c --- /dev/null +++ b/visual/icons/DirMovies.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bix0itrhquk43" +path="res://.godot/imported/DirMovies.svg-6502b6ed30bc262e78e211e2590f4935.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirMovies.svg" +dest_files=["res://.godot/imported/DirMovies.svg-6502b6ed30bc262e78e211e2590f4935.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/DirMusic.svg b/visual/icons/DirMusic.svg new file mode 100644 index 00000000..f5cfcd19 --- /dev/null +++ b/visual/icons/DirMusic.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirMusic.svg.import b/visual/icons/DirMusic.svg.import new file mode 100644 index 00000000..5cb39154 --- /dev/null +++ b/visual/icons/DirMusic.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://yspvgvx4p5i7" +path="res://.godot/imported/DirMusic.svg-1579bce19495d8501e24ddf74840513b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirMusic.svg" +dest_files=["res://.godot/imported/DirMusic.svg-1579bce19495d8501e24ddf74840513b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/DirPictures.svg b/visual/icons/DirPictures.svg new file mode 100644 index 00000000..86153c63 --- /dev/null +++ b/visual/icons/DirPictures.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/DirPictures.svg.import b/visual/icons/DirPictures.svg.import new file mode 100644 index 00000000..2e8e2773 --- /dev/null +++ b/visual/icons/DirPictures.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ctulth0ybk2l7" +path="res://.godot/imported/DirPictures.svg-031ed7212e88372d0a1bdba18111386e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/DirPictures.svg" +dest_files=["res://.godot/imported/DirPictures.svg-031ed7212e88372d0a1bdba18111386e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/Folder.svg b/visual/icons/Folder.svg new file mode 100644 index 00000000..53891da2 --- /dev/null +++ b/visual/icons/Folder.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/Folder.svg.import b/visual/icons/Folder.svg.import new file mode 100644 index 00000000..74a588f4 --- /dev/null +++ b/visual/icons/Folder.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cba8p7b8tjiv3" +path="res://.godot/imported/Folder.svg-82fa99ce41a31c247dc49e3ac68cce3f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/Folder.svg" +dest_files=["res://.godot/imported/Folder.svg-82fa99ce41a31c247dc49e3ac68cce3f.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/theme/FolderUp.svg b/visual/icons/FolderUp.svg similarity index 100% rename from visual/icons/theme/FolderUp.svg rename to visual/icons/FolderUp.svg diff --git a/visual/icons/theme/FolderUp.svg.import b/visual/icons/FolderUp.svg.import similarity index 74% rename from visual/icons/theme/FolderUp.svg.import rename to visual/icons/FolderUp.svg.import index 8f80bf9d..abe92ee2 100644 --- a/visual/icons/theme/FolderUp.svg.import +++ b/visual/icons/FolderUp.svg.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://rrhdja8l17cn" -path="res://.godot/imported/FolderUp.svg-b4eb7c260434a037353caaa49dc2e4f8.ctex" +path="res://.godot/imported/FolderUp.svg-ea80ed3af2557e3c1b26b83ba445e18e.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://visual/icons/theme/FolderUp.svg" -dest_files=["res://.godot/imported/FolderUp.svg-b4eb7c260434a037353caaa49dc2e4f8.ctex"] +source_file="res://visual/icons/FolderUp.svg" +dest_files=["res://.godot/imported/FolderUp.svg-ea80ed3af2557e3c1b26b83ba445e18e.ctex"] [params] diff --git a/visual/icons/Search.svg b/visual/icons/Search.svg new file mode 100644 index 00000000..73a40c91 --- /dev/null +++ b/visual/icons/Search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/visual/icons/Search.svg.import b/visual/icons/Search.svg.import new file mode 100644 index 00000000..1b2c30ec --- /dev/null +++ b/visual/icons/Search.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d4c7haflm8evm" +path="res://.godot/imported/Search.svg-2dd99ab889ce67e689ca0f3320130c17.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://visual/icons/Search.svg" +dest_files=["res://.godot/imported/Search.svg-2dd99ab889ce67e689ca0f3320130c17.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/visual/icons/tag/polyline.svg b/visual/icons/tag/polyline.svg index c973db42..b33495f8 100644 --- a/visual/icons/tag/polyline.svg +++ b/visual/icons/tag/polyline.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file