diff --git a/src/Autoload/Export.gd b/src/Autoload/Export.gd index 4888e703d..21d939751 100644 --- a/src/Autoload/Export.gd +++ b/src/Autoload/Export.gd @@ -10,6 +10,7 @@ enum FileFormat { PNG, WEBP, JPEG, GIF, APNG } var animated_formats := [FileFormat.GIF, FileFormat.APNG] ## A dictionary of custom exporter generators (received from extensions) +var custom_file_formats := {} var custom_exporter_generators := {} var current_tab := ExportTab.IMAGE @@ -51,16 +52,43 @@ func _multithreading_enabled() -> bool: return ProjectSettings.get_setting("rendering/driver/threads/thread_model") == 2 -func add_file_format(_format: String) -> int: - var id := FileFormat.size() -# FileFormat.merge({format: id}) +func add_custom_file_format( + format_name: String, extension: String, exporter_generator: Object, tab: int, is_animated: bool +) -> int: + # Obtain a unique id + var id := Export.FileFormat.size() + for i in Export.custom_file_formats.size(): + var format_id = id + i + if !Export.custom_file_formats.values().has(i): + id = format_id + # Add to custom_file_formats + custom_file_formats.merge({format_name: id}) + custom_exporter_generators.merge({id: [exporter_generator, extension]}) + if is_animated: + Export.animated_formats.append(id) + # Add to export dialog + match tab: + ExportTab.IMAGE: + Global.export_dialog.image_exports.append(id) + ExportTab.SPRITESHEET: + Global.export_dialog.spritesheet_exports.append(id) + _: # Both + Global.export_dialog.image_exports.append(id) + Global.export_dialog.spritesheet_exports.append(id) return id -func remove_file_format(id: int) -> void: - for key in Export.FileFormat.keys(): - if Export.FileFormat[key] == id: -# Export.FileFormat.erase(key) +func remove_custom_file_format(id: int) -> void: + for key in custom_file_formats.keys(): + if custom_file_formats[key] == id: + custom_file_formats.erase(key) + # remove exporter generator + Export.custom_exporter_generators.erase(id) + # remove from animated (if it is present there) + Export.animated_formats.erase(id) + # remove from export dialog + Global.export_dialog.image_exports.erase(id) + Global.export_dialog.spritesheet_exports.erase(id) return @@ -392,8 +420,8 @@ func file_format_description(format_enum: int) -> String: return "APNG Image" _: # If a file format description is not found, try generating one - for key in FileFormat.keys(): - if FileFormat[key] == format_enum: + for key in custom_file_formats.keys(): + if custom_file_formats[key] == format_enum: return str(key.capitalize()) return "" diff --git a/src/Autoload/ExtensionsAPI.gd b/src/Autoload/ExtensionsAPI.gd index 6644a90fd..e63052ac3 100644 --- a/src/Autoload/ExtensionsAPI.gd +++ b/src/Autoload/ExtensionsAPI.gd @@ -476,57 +476,37 @@ class ExportAPI: var ExportTab := Export.ExportTab func add_export_option( - format_info: Dictionary, exporter_generator, tab := ExportTab.IMAGE, is_animated := true + format_info: Dictionary, + exporter_generator: Object, + tab := ExportTab.IMAGE, + is_animated := true ) -> int: - # separate enum name and file name + # Separate enum name and file name var extension = "" var format_name = "" if format_info.has("extension"): extension = format_info["extension"] if format_info.has("description"): - format_name = format_info["description"].to_upper().replace(" ", "_") - # change format name if another one uses the same name - for i in range(Export.FileFormat.size()): + format_name = format_info["description"].strip_edges().to_upper().replace(" ", "_") + # Change format name if another one uses the same name + var existing_format_names = Export.FileFormat.keys() + Export.custom_file_formats.keys() + for i in range(existing_format_names.size()): var test_name = format_name if i != 0: test_name = str(test_name, "_", i) - if !Export.FileFormat.keys().has(test_name): + if !existing_format_names.has(test_name): format_name = test_name break - # add to FileFormat enum - var id := Export.FileFormat.size() - for i in Export.FileFormat.size(): # use an empty id if it's available - if !Export.FileFormat.values().has(i): - id = i -# Export.FileFormat.merge({format_name: id}) - # add exporter generator - Export.custom_exporter_generators.merge({id: [exporter_generator, extension]}) - # add to animated (or not) - if is_animated: - Export.animated_formats.append(id) - # add to export dialog - match tab: - ExportTab.IMAGE: - Global.export_dialog.image_exports.append(id) - ExportTab.SPRITESHEET: - Global.export_dialog.spritesheet_exports.append(id) - _: # Both - Global.export_dialog.image_exports.append(id) - Global.export_dialog.spritesheet_exports.append(id) + # Setup complete, add the exporter + var id = Export.add_custom_file_format( + format_name, extension, exporter_generator, tab, is_animated + ) ExtensionsApi.add_action("add_exporter") return id func remove_export_option(id: int): if Export.custom_exporter_generators.has(id): - # remove enum - Export.remove_file_format(id) - # remove exporter generator - Export.custom_exporter_generators.erase(id) - # remove from animated (or not) - Export.animated_formats.erase(id) - # add to export dialog - Global.export_dialog.image_exports.erase(id) - Global.export_dialog.spritesheet_exports.erase(id) + Export.remove_custom_file_format(id) ExtensionsApi.remove_action("add_exporter") @@ -623,3 +603,12 @@ class SignalsAPI: func disconnect_current_cel_texture_changed(callable: Callable): texture_changed.disconnect(callable) ExtensionsApi.remove_action("texture_changed") + + # Export dialog signals + func connect_export_about_to_preview(target: Object, method: String): + Global.export_dialog.about_to_preview.connect(Callable(target, method)) + ExtensionsApi.add_action("export_about_to_preview") + + func disconnect_export_about_to_preview(target: Object, method: String): + Global.export_dialog.about_to_preview.disconnect(Callable(target, method)) + ExtensionsApi.remove_action("export_about_to_preview") diff --git a/src/Preferences/HandleExtensions.gd b/src/Preferences/HandleExtensions.gd index 276d3802b..6e441f6e4 100644 --- a/src/Preferences/HandleExtensions.gd +++ b/src/Preferences/HandleExtensions.gd @@ -166,7 +166,11 @@ func _add_extension(file_name: String) -> void: "The extension %s will not work on this version of Pixelorama \n" % file_name_no_ext ) - var required_text := "Requires API version: %s" % str(supported_api_versions) + var required_text := str( + "Extension works on API versions: %s" % str(supported_api_versions), + "\n", + "But Pixelorama's API version is: %s" % ExtensionsApi.get_api_version() + ) Global.error_dialog.set_text(str(err_text, required_text)) Global.error_dialog.popup_centered() Global.dialog_open(true) diff --git a/src/UI/Dialogs/ExportDialog.gd b/src/UI/Dialogs/ExportDialog.gd index 1108868b9..56edb5125 100644 --- a/src/UI/Dialogs/ExportDialog.gd +++ b/src/UI/Dialogs/ExportDialog.gd @@ -2,6 +2,7 @@ extends ConfirmationDialog ## Called when user resumes export after filename collision signal resume_export_function +signal about_to_preview(Dictionary) var preview_current_frame := 0 var preview_frames: Array[Texture2D] = [] @@ -18,6 +19,9 @@ var spritesheet_exports: Array[Export.FileFormat] = [ Export.FileFormat.PNG, Export.FileFormat.WEBP, Export.FileFormat.JPEG ] +var _preview_images: Array[Image] +var _preview_durations: PackedFloat32Array + @onready var tabs: TabBar = $VBoxContainer/TabBar @onready var checker: ColorRect = $"%TransparentChecker" @onready var previews: GridContainer = $"%Previews" @@ -91,18 +95,27 @@ func show_tab() -> void: func set_preview() -> void: + _preview_images = Export.processed_images.duplicate() + _preview_durations = Export.durations.duplicate() + var preview_data = { + "exporter_id": Global.current_project.file_format, + "export_tab": Export.current_tab, + "preview_images": _preview_images, + "durations": _preview_durations + } + about_to_preview.emit(preview_data) remove_previews() - if Export.processed_images.size() == 1: + if _preview_images.size() == 1: previews.columns = 1 - add_image_preview(Export.processed_images[0]) + add_image_preview(_preview_images[0]) else: if Export.is_single_file_format(): previews.columns = 1 add_animated_preview() else: - previews.columns = ceili(sqrt(Export.processed_images.size())) - for i in range(Export.processed_images.size()): - add_image_preview(Export.processed_images[i], i + 1) + previews.columns = ceili(sqrt(_preview_images.size())) + for i in range(_preview_images.size()): + add_image_preview(_preview_images[i], i + 1) if Global.current_project.file_format == Export.FileFormat.GIF: $"%GifWarning".visible = true @@ -133,7 +146,7 @@ func add_animated_preview() -> void: preview_current_frame = 0 preview_frames = [] - for processed_image in Export.processed_images: + for processed_image in _preview_images: var texture := ImageTexture.create_from_image(processed_image) preview_frames.push_back(texture) @@ -146,7 +159,7 @@ func add_animated_preview() -> void: previews.add_child(container) frame_timer.set_one_shot(true) # wait_time can't change correctly if the timer is playing - frame_timer.wait_time = Export.durations[preview_current_frame] + frame_timer.wait_time = _preview_durations[preview_current_frame] frame_timer.start() @@ -226,8 +239,8 @@ func create_layer_list() -> void: func update_dimensions_label() -> void: - if Export.processed_images.size() > 0: - var new_size: Vector2 = Export.processed_images[0].get_size() * (Export.resize / 100.0) + if _preview_images.size() > 0: + var new_size: Vector2 = _preview_images[0].get_size() * (Export.resize / 100.0) dimension_label.text = str(new_size.x, "×", new_size.y) @@ -390,7 +403,7 @@ func _on_FrameTimer_timeout() -> void: else: preview_current_frame += 1 - frame_timer.wait_time = Export.durations[preview_current_frame - 1] + frame_timer.wait_time = _preview_durations[preview_current_frame - 1] frame_timer.start() diff --git a/src/UI/Dialogs/ExportDialog.tscn b/src/UI/Dialogs/ExportDialog.tscn index b0c3f1286..a2b37b7c8 100644 --- a/src/UI/Dialogs/ExportDialog.tscn +++ b/src/UI/Dialogs/ExportDialog.tscn @@ -8,6 +8,7 @@ [node name="ExportDialog" type="ConfirmationDialog"] canvas_item_default_texture_filter = 0 title = "Export..." +position = Vector2i(0, 36) size = Vector2i(700, 600) exclusive = false dialog_hide_on_ok = false @@ -17,8 +18,8 @@ script = ExtResource("1") custom_minimum_size = Vector2(330, 0) offset_left = 8.0 offset_top = 8.0 -offset_right = 524.0 -offset_bottom = 494.0 +offset_right = 692.0 +offset_bottom = 551.0 size_flags_vertical = 3 [node name="TabBar" type="TabBar" parent="VBoxContainer"] @@ -300,8 +301,8 @@ layout_mode = 3 anchors_preset = 0 offset_left = 8.0 offset_top = 8.0 -offset_right = 524.0 -offset_bottom = 494.0 +offset_right = 692.0 +offset_bottom = 551.0 mouse_filter = 2 [node name="PathDialog" type="FileDialog" parent="Popups"]