1
0
Fork 0
mirror of https://github.com/Orama-Interactive/Pixelorama.git synced 2025-01-30 23:19:49 +00:00

[ExtensionsAPI] Implement an Import API (#957)

* Added ImportAPI

* Delete src/UI/Dialogs/PreviewDialog.gd

* Delete src/UI/Dialogs/PreviewDialog.tscn
This commit is contained in:
Variable 2023-12-12 19:04:49 +05:00 committed by GitHub
parent 8e1d949e08
commit 17ee10a131
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 487 additions and 387 deletions

View file

@ -26,7 +26,8 @@ var theme := ThemeAPI.new() ## Gives access to theme related functions.
var tools := ToolAPI.new() ## Gives ability to add/remove tools.
var selection := SelectionAPI.new() ## Gives access to pixelorama's selection system.
var project := ProjectAPI.new() ## Gives access to project manipulation.
var exports := ExportAPI.new() ## Gives access to adding custom exporters.
var export := ExportAPI.new() ## Gives access to adding custom exporters.
var import := ImportAPI.new() ## Gives access to adding custom exporters.
var signals := SignalsAPI.new() ## Gives access to the basic commonly used signals.
## This fail-safe below is designed to work ONLY if Pixelorama is launched in Godot Editor
@ -102,6 +103,18 @@ func get_api_version() -> int:
return ProjectSettings.get_setting("application/config/ExtensionsAPI_Version")
## Returns the initial nodes of an extension named [param extension_name].
## initial nodes are the nodes whose paths are in the [code]nodes[/code] key of an
## extension.json file.
func get_main_nodes(extension_name: StringName) -> Array[Node]:
var extensions_node = Global.control.get_node("Extensions")
var nodes: Array[Node] = []
for child: Node in extensions_node.get_children():
if child.is_in_group(extension_name):
nodes.append(child)
return nodes
## Gives Access to the general stuff.
##
## This part of Api provides stuff like commonly used Autoloads, App's version info etc
@ -656,6 +669,33 @@ class ExportAPI:
ExtensionsApi.remove_action("add_exporter")
## Gives access to adding custom import options.
class ImportAPI:
## [param import_scene] is a scene preload that will be instanced and added to "import options"
## section of pixelorama's import dialogs and will appears whenever [param import_name] is
## chosen from import menu.
## [br]
## [param import_scene] must have a a script containing:[br]
## 1. An optional variable named [code]import_preview_dialog[/code] of type [ConfirmationDialog],
## If present, it will automatically be assigned a reference to the relevant import dialog's
## [code]ImportPreviewDialog[/code] class so that you can easily access variables and
## methods of that class. (This variable is meant to be read-only)[br]
## 2. The method [method initiate_import] which takes 2 arguments: [code]path[/code],
## [code]image[/code], which are automatically passed to [method initiate_import] at
## time of import.
func add_import_option(import_name: StringName, import_scene_preload: PackedScene):
var id = OpenSave.add_import_option(import_name, import_scene_preload)
ExtensionsApi.add_action("add_import_option")
return id
## Removes the import option with [param id] from Pixelorama.
func remove_import_option(id: int):
var import_name = OpenSave.custom_import_names.find_key(id)
OpenSave.custom_import_names.erase(import_name)
OpenSave.custom_importer_scenes.erase(id)
ExtensionsApi.remove_action("add_import_option")
## Gives access to the basic commonly used signals.
##
## Gives access to the basic commonly used signals.

View file

@ -7,11 +7,15 @@ signal reference_image_imported
var current_save_paths: PackedStringArray = []
## Stores a filename of a backup file in user:// until user saves manually
var backup_save_paths: PackedStringArray = []
var preview_dialog_tscn := preload("res://src/UI/Dialogs/PreviewDialog.tscn")
var preview_dialog_tscn := preload("res://src/UI/Dialogs/ImportPreviewDialog.tscn")
var preview_dialogs := [] ## Array of preview dialogs
var last_dialog_option := 0
var autosave_timer: Timer
# custom importer related dictionaries (received from extensions)
var custom_import_names := {} ## Contains importer names as keys and ids as values
var custom_importer_scenes := {} ## Contains ids keys and import option preloads as values
func _ready() -> void:
autosave_timer = Timer.new()
@ -72,8 +76,38 @@ func handle_loading_file(file: String) -> void:
handle_loading_image(file, image)
func add_import_option(import_name: StringName, import_scene: PackedScene) -> int:
# Change format name if another one uses the same name
var existing_format_names = (
ImportPreviewDialog.ImageImportOptions.keys() + custom_import_names.keys()
)
for i in range(existing_format_names.size()):
var test_name = import_name
if i != 0:
test_name = str(test_name, "_", i)
if !existing_format_names.has(test_name):
import_name = test_name
break
# Obtain a unique id
var id := ImportPreviewDialog.ImageImportOptions.size()
for i in custom_import_names.size():
var format_id = id + i
if !custom_import_names.values().has(i):
id = format_id
# Add to custom_file_formats
custom_import_names.merge({import_name: id})
custom_importer_scenes.merge({id: import_scene})
return id
func handle_loading_image(file: String, image: Image) -> void:
var preview_dialog := preview_dialog_tscn.instantiate() as PreviewDialog
var preview_dialog := preview_dialog_tscn.instantiate() as ImportPreviewDialog
# add custom importers to preview dialog
for import_name in custom_import_names.keys():
var id = custom_import_names[import_name]
var new_import_option = custom_importer_scenes[id].instantiate()
preview_dialog.custom_importers[id] = new_import_option
preview_dialogs.append(preview_dialog)
preview_dialog.path = file
preview_dialog.image = image
@ -630,17 +664,15 @@ func open_image_at_cel(image: Image, layer_index := 0, frame_index := 0) -> void
project.undos += 1
project.undo_redo.create_action("Replaced Cel")
for i in project.frames.size():
if i == frame_index:
image.convert(Image.FORMAT_RGBA8)
var cel := project.frames[i].cels[layer_index]
if not cel is PixelCel:
continue
var cel_image := Image.create(project_width, project_height, false, Image.FORMAT_RGBA8)
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
Global.undo_redo_compress_images(
{cel.image: cel_image.data}, {cel.image: cel.image.data}, project
)
var cel := project.frames[frame_index].cels[layer_index]
if not cel is PixelCel:
return
image.convert(Image.FORMAT_RGBA8)
var cel_image := Image.create(project_width, project_height, false, Image.FORMAT_RGBA8)
cel_image.blit_rect(image, Rect2i(Vector2i.ZERO, image.get_size()), Vector2i.ZERO)
Global.undo_redo_compress_images(
{cel.image: cel_image.data}, {cel.image: cel.image.data}, project
)
project.undo_redo.add_do_property(project, "selected_cels", [])
project.undo_redo.add_do_method(project.change_cel.bind(frame_index, layer_index))

View file

@ -1,4 +1,4 @@
class_name PreviewDialog
class_name ImportPreviewDialog
extends ConfirmationDialog
enum ImageImportOptions {
@ -28,55 +28,72 @@ var opened_once := false
var is_main := false
var hiding := false
@onready var texture_rect: TextureRect = $VBoxContainer/AspectRatioContainer/TextureRect
@onready var image_size_label: Label = $VBoxContainer/SizeContainer/ImageSizeLabel
@onready var frame_size_label: Label = $VBoxContainer/SizeContainer/FrameSizeLabel
@onready var smart_slice_checkbox := %SmartSliceButton as CheckBox
@onready var merge_threshold := %SmartOptions/Threshold as ValueSlider
@onready var merge_dist := %SmartOptions/MergeDist as ValueSlider
@onready
var spritesheet_manual_tab_options = $VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual
@onready var spritesheet_smart_tab_options := %SmartOptions as HBoxContainer
@onready var spritesheet_tab_options = $VBoxContainer/HBoxContainer/SpritesheetTabOptions
@onready var spritesheet_lay_opt = $VBoxContainer/HBoxContainer/SpritesheetLayerOptions
@onready var new_frame_options = $VBoxContainer/HBoxContainer/NewFrameOptions
@onready var replace_cel_options = $VBoxContainer/HBoxContainer/ReplaceCelOptions
@onready var new_layer_options = $VBoxContainer/HBoxContainer/NewLayerOptions
@onready var new_brush_options = $VBoxContainer/HBoxContainer/NewBrushOptions
@onready var new_brush_name = $VBoxContainer/HBoxContainer/NewBrushOptions/BrushName
## keeps track of which custom export to show when it's import option is selected
## Contains ids as keys and custion import option scenes as values
var custom_importers = {}
@onready var import_options: OptionButton = $VBoxContainer/HBoxContainer/ImportOption
@onready var apply_all: CheckBox = $VBoxContainer/ApplyAll
## A [TextureRect] containing the preview image
@onready var texture_rect: TextureRect = %TextureRect
## The [OptionButton] containing import options
@onready var import_option_button: OptionButton = %ImportOption
## A [CheckBox] for enabling apply all system.
@onready var apply_all: CheckBox = $VBoxContainer/VBoxContainer/ApplyAll
## Label showing size of original image.
@onready var image_size_label: Label = $VBoxContainer/VBoxContainer/SizeContainer/ImageSizeLabel
## Label for showing size of image after import.
@onready var frame_size_label: Label = $VBoxContainer/VBoxContainer/SizeContainer/FrameSizeLabel
## Container for all types of advanced settings like [member spritesheet_options],
## [member new_brush_options] etc...
@onready var import_options: VBoxContainer = %ImportOptions
# Below are some common settings grouped into categories and are made visible/invisible
# depending on what your import option requires.
## container of spritesheet related import options
@onready var spritesheet_options = %ImportOptions/SpritesheetOptions
## container of frame related import options
@onready var at_frame_option = %ImportOptions/AtFrame
## container of layer related import options
@onready var at_layer_option = %ImportOptions/AtLayer
## container of brush related import options
@onready var new_brush_options = %ImportOptions/NewBrushOptions
func _on_PreviewDialog_about_to_show() -> void:
func _on_ImportPreviewDialog_about_to_show() -> void:
if opened_once:
return
opened_once = true
# # order as in ImageImportOptions enum
import_options.add_item("New project")
import_options.add_item("Spritesheet (new project)")
import_options.add_item("Spritesheet (new layer)")
import_options.add_item("New frame")
import_options.add_item("Replace cel")
import_options.add_item("New layer")
import_options.add_item("New reference image")
import_options.add_item("New palette")
import_options.add_item("New brush")
import_options.add_item("New pattern")
import_option_button.add_item("New project")
import_option_button.add_item("Spritesheet (new project)")
import_option_button.add_item("Spritesheet (new layer)")
import_option_button.add_item("New frame")
import_option_button.add_item("Replace cel")
import_option_button.add_item("New layer")
import_option_button.add_item("New reference image")
import_option_button.add_item("New palette")
import_option_button.add_item("New brush")
import_option_button.add_item("New pattern")
# adding custom importers
for id in custom_importers.keys():
var scene = custom_importers[id]
var import_name = OpenSave.custom_import_names.find_key(id)
scene.set("import_preview_dialog", self)
import_options.add_child(scene)
import_option_button.add_item(import_name, id)
# Select the option that the preview dialog before it had selected
import_options.select(OpenSave.last_dialog_option)
import_options.item_selected.emit(OpenSave.last_dialog_option)
import_option_button.select(OpenSave.last_dialog_option)
import_option_button.item_selected.emit(import_option_button.selected)
var img_texture := ImageTexture.create_from_image(image)
texture_rect.texture = img_texture
spritesheet_manual_tab_options.get_node("HorizontalFrames").max_value = min(
spritesheet_manual_tab_options.get_node("HorizontalFrames").max_value, image.get_size().x
)
spritesheet_manual_tab_options.get_node("VerticalFrames").max_value = min(
spritesheet_manual_tab_options.get_node("VerticalFrames").max_value, image.get_size().y
)
# set max values of spritesheet options
var h_frames = spritesheet_options.find_child("HorizontalFrames")
var v_frames = spritesheet_options.find_child("VerticalFrames")
h_frames.max_value = min(h_frames.max_value, image.get_size().x)
v_frames.max_value = min(v_frames.max_value, image.get_size().y)
# set labels
image_size_label.text = (
tr("Image Size") + ": " + str(image.get_size().x) + "×" + str(image.get_size().y)
)
@ -94,7 +111,7 @@ func _on_visibility_changed() -> void:
return
elif is_main: # if the main dialog is closed then close others too
for child in Global.control.get_children():
if child is PreviewDialog:
if child is ImportPreviewDialog:
OpenSave.preview_dialogs.erase(child)
child.queue_free()
else: # dialogs being closed separately
@ -106,12 +123,12 @@ func _on_visibility_changed() -> void:
Global.dialog_open(false)
func _on_PreviewDialog_confirmed() -> void:
func _on_ImportPreviewDialog_confirmed() -> void:
if is_main: # if the main dialog is confirmed then confirm others too
is_main = false
synchronize()
for child in Global.control.get_children():
if child is PreviewDialog:
if child is ImportPreviewDialog:
child.confirmed.emit()
else:
if current_import_option == ImageImportOptions.NEW_TAB:
@ -130,7 +147,7 @@ func _on_PreviewDialog_confirmed() -> void:
)
elif current_import_option == ImageImportOptions.SPRITESHEET_LAYER:
var frame_index: int = spritesheet_lay_opt.get_node("AtFrameSpinbox").value - 1
var frame_index: int = at_frame_option.get_node("AtFrameSpinbox").value - 1
if smart_slice:
if !recycle_last_slice_result:
obtain_sliced_data()
@ -153,16 +170,16 @@ func _on_PreviewDialog_confirmed() -> void:
)
elif current_import_option == ImageImportOptions.NEW_FRAME:
var layer_index: int = new_frame_options.get_node("AtLayerOption").get_selected_id()
var layer_index: int = at_layer_option.get_node("AtLayerOption").get_selected_id()
OpenSave.open_image_as_new_frame(image, layer_index)
elif current_import_option == ImageImportOptions.REPLACE_CEL:
var layer_index: int = replace_cel_options.get_node("AtLayerOption").get_selected_id()
var frame_index: int = replace_cel_options.get_node("AtFrameSpinbox").value - 1
var layer_index: int = at_layer_option.get_node("AtLayerOption").get_selected_id()
var frame_index: int = at_frame_option.get_node("AtFrameSpinbox").value - 1
OpenSave.open_image_at_cel(image, layer_index, frame_index)
elif current_import_option == ImageImportOptions.NEW_LAYER:
var frame_index: int = new_layer_options.get_node("AtFrameSpinbox").value - 1
var frame_index: int = at_frame_option.get_node("AtFrameSpinbox").value - 1
OpenSave.open_image_as_new_layer(image, path.get_basename().get_file(), frame_index)
elif current_import_option == ImageImportOptions.NEW_REFERENCE_IMAGE:
@ -189,6 +206,12 @@ func _on_PreviewDialog_confirmed() -> void:
var dir := DirAccess.open(path.get_base_dir())
dir.copy(path, Global.home_data_directory.path_join(location))
else:
if current_import_option in custom_importers.keys():
var importer = custom_importers[current_import_option]
if importer.has_method("initiate_import"):
importer.call("initiate_import", path, image)
func _on_ApplyAll_toggled(pressed: bool) -> void:
is_main = pressed
@ -198,7 +221,7 @@ func _on_ApplyAll_toggled(pressed: bool) -> void:
hide()
visibility_changed.connect(_on_visibility_changed)
for child in Global.control.get_children():
if child != self and child is PreviewDialog:
if child != self and child is ImportPreviewDialog:
child.hiding = pressed
if pressed:
child.hide()
@ -210,47 +233,45 @@ func _on_ApplyAll_toggled(pressed: bool) -> void:
func synchronize() -> void:
for child in Global.control.get_children():
if child != self and child is PreviewDialog:
var dialog := child as PreviewDialog
if child != self and child is ImportPreviewDialog:
var dialog := child as ImportPreviewDialog
#sync modes
var id := current_import_option
dialog.import_options.select(id)
dialog.import_options.item_selected.emit(id)
dialog.import_option_button.select(id)
dialog.import_option_button.item_selected.emit(id)
#sync properties (if any)
if (
id == ImageImportOptions.SPRITESHEET_TAB
or id == ImageImportOptions.SPRITESHEET_LAYER
):
dialog.spritesheet_manual_tab_options.get_node("HorizontalFrames").value = min(
spritesheet_manual_tab_options.get_node("HorizontalFrames").value,
image.get_size().x
)
dialog.spritesheet_manual_tab_options.get_node("VerticalFrames").value = min(
spritesheet_manual_tab_options.get_node("VerticalFrames").value,
image.get_size().y
)
var h_frames = spritesheet_options.find_child("HorizontalFrames")
var v_frames = spritesheet_options.find_child("VerticalFrames")
var d_h_frames = dialog.spritesheet_options.find_child("HorizontalFrames")
var d_v_frames = dialog.spritesheet_options.find_child("VerticalFrames")
d_h_frames.value = min(h_frames.value, image.get_size().x)
d_v_frames.value = min(v_frames.value, image.get_size().y)
if id == ImageImportOptions.SPRITESHEET_LAYER:
dialog.spritesheet_lay_opt.get_node("AtFrameSpinbox").value = (
spritesheet_lay_opt.get_node("AtFrameSpinbox").value
dialog.at_frame_option.get_node("AtFrameSpinbox").value = (
at_frame_option.get_node("AtFrameSpinbox").value
)
elif id == ImageImportOptions.NEW_FRAME:
dialog.new_frame_options.get_node("AtLayerOption").selected = (
new_frame_options.get_node("AtLayerOption").selected
dialog.at_layer_option.get_node("AtLayerOption").selected = (
at_layer_option.get_node("AtLayerOption").selected
)
elif id == ImageImportOptions.REPLACE_CEL:
dialog.replace_cel_options.get_node("AtLayerOption").selected = (
replace_cel_options.get_node("AtLayerOption").selected
dialog.at_layer_option.get_node("AtLayerOption").selected = (
at_layer_option.get_node("AtLayerOption").selected
)
dialog.replace_cel_options.get_node("AtFrameSpinbox").value = (
replace_cel_options.get_node("AtFrameSpinbox").value
dialog.at_frame_option.get_node("AtFrameSpinbox").value = (
at_frame_option.get_node("AtFrameSpinbox").value
)
elif id == ImageImportOptions.NEW_LAYER:
dialog.new_layer_options.get_node("AtFrameSpinbox").value = (
new_layer_options.get_node("AtFrameSpinbox").value
dialog.at_frame_option.get_node("AtFrameSpinbox").value = (
at_frame_option.get_node("AtFrameSpinbox").value
)
elif id == ImageImportOptions.BRUSH:
@ -259,78 +280,93 @@ func synchronize() -> void:
dialog.new_brush_options.get_node("BrushTypeOption").item_selected.emit(type)
func _hide_all_options():
# reset some options
smart_slice = false
apply_all.disabled = false
spritesheet_options.get_node("SmartSliceToggle").button_pressed = false
at_frame_option.get_node("AtFrameSpinbox").allow_greater = false
setup_smart_slice(false)
# hide items
texture_rect.get_child(0).visible = false
texture_rect.get_child(1).visible = false
for child: Node in import_options.get_children():
child.visible = false
func _on_ImportOption_item_selected(id: ImageImportOptions) -> void:
current_import_option = id
OpenSave.last_dialog_option = current_import_option
smart_slice_checkbox.button_pressed = false
apply_all.disabled = false
smart_slice = false
smart_slice_checkbox.visible = false
spritesheet_tab_options.visible = false
spritesheet_lay_opt.visible = false
new_frame_options.visible = false
replace_cel_options.visible = false
new_layer_options.visible = false
new_brush_options.visible = false
texture_rect.get_child(0).visible = false
texture_rect.get_child(1).visible = false
_hide_all_options()
import_options.get_parent().visible = true
if id == ImageImportOptions.SPRITESHEET_TAB:
frame_size_label.visible = true
smart_slice_checkbox.visible = true
spritesheet_tab_options.visible = true
spritesheet_options.visible = true
texture_rect.get_child(0).visible = true
texture_rect.get_child(1).visible = true
elif id == ImageImportOptions.SPRITESHEET_LAYER:
frame_size_label.visible = true
smart_slice_checkbox.visible = true
spritesheet_lay_opt.visible = true
spritesheet_tab_options.visible = true
at_frame_option.visible = true
spritesheet_options.visible = true
texture_rect.get_child(0).visible = true
texture_rect.get_child(1).visible = true
at_frame_option.get_node("AtFrameSpinbox").allow_greater = true
elif id == ImageImportOptions.NEW_FRAME:
new_frame_options.visible = true
at_layer_option.visible = true
# Fill the at layer option button:
var at_layer_option: OptionButton = new_frame_options.get_node("AtLayerOption")
at_layer_option.clear()
var at_layer_option_button: OptionButton = at_layer_option.get_node("AtLayerOption")
at_layer_option_button.clear()
var layers := Global.current_project.layers.duplicate()
layers.reverse()
var i := 0
for l in layers:
if not l is PixelLayer:
continue
at_layer_option.add_item(l.name, l.index)
at_layer_option.set_item_tooltip(i, l.get_layer_path())
at_layer_option_button.add_item(l.name, l.index)
at_layer_option_button.set_item_tooltip(i, l.get_layer_path())
i += 1
at_layer_option.selected = at_layer_option.get_item_count() - 1
at_layer_option_button.selected = at_layer_option_button.get_item_count() - 1
elif id == ImageImportOptions.REPLACE_CEL:
replace_cel_options.visible = true
at_frame_option.visible = true
at_layer_option.visible = true
# Fill the at layer option button:
var at_layer_option: OptionButton = replace_cel_options.get_node("AtLayerOption")
at_layer_option.clear()
var at_layer_option_button: OptionButton = at_layer_option.get_node("AtLayerOption")
at_layer_option_button.clear()
var layers := Global.current_project.layers.duplicate()
layers.reverse()
var i := 0
for l in layers:
if not l is PixelLayer:
continue
at_layer_option.add_item(l.name, l.index)
at_layer_option.set_item_tooltip(i, l.get_layer_path())
at_layer_option_button.add_item(l.name, l.index)
at_layer_option_button.set_item_tooltip(i, l.get_layer_path())
i += 1
at_layer_option.selected = at_layer_option.get_item_count() - 1
var at_frame_spinbox: SpinBox = replace_cel_options.get_node("AtFrameSpinbox")
at_layer_option_button.selected = at_layer_option_button.get_item_count() - 1
var at_frame_spinbox: SpinBox = at_frame_option.get_node("AtFrameSpinbox")
at_frame_spinbox.max_value = Global.current_project.frames.size()
elif id == ImageImportOptions.NEW_LAYER:
new_layer_options.visible = true
new_layer_options.get_node("AtFrameSpinbox").max_value = (
at_frame_option.visible = true
at_frame_option.get_node("AtFrameSpinbox").max_value = (
Global.current_project.frames.size()
)
elif id == ImageImportOptions.BRUSH:
new_brush_options.visible = true
else:
if id in ImageImportOptions.values():
import_options.get_parent().visible = false
else:
if is_main: # Disable apply all (for import options added by extension)
apply_all.button_pressed = false
apply_all.disabled = true
if id in custom_importers.keys():
custom_importers[id].visible = true
_call_queue_redraw()
@ -339,8 +375,8 @@ func _on_smart_slice_toggled(button_pressed: bool) -> void:
func setup_smart_slice(enabled: bool) -> void:
spritesheet_smart_tab_options.visible = enabled
spritesheet_manual_tab_options.visible = !enabled
spritesheet_options.get_node("Manual").visible = !enabled
spritesheet_options.get_node("Smart").visible = enabled
if is_main: # Disable apply all (the algorithm is not fast enough for this)
apply_all.button_pressed = false
apply_all.disabled = enabled
@ -351,6 +387,8 @@ func setup_smart_slice(enabled: bool) -> void:
func obtain_sliced_data() -> void:
var merge_threshold := spritesheet_options.find_child("Threshold") as ValueSlider
var merge_dist := spritesheet_options.find_child("MergeDist") as ValueSlider
var unpak := RegionUnpacker.new(merge_threshold.value, merge_dist.value)
sliced_rects = unpak.get_used_rects(texture_rect.texture.get_image())
@ -396,9 +434,9 @@ func spritesheet_frame_value_changed() -> void:
func _on_BrushTypeOption_item_selected(index: BrushTypes) -> void:
brush_type = index
new_brush_name.visible = false
new_brush_options.get_node("BrushName").visible = false
if brush_type == BrushTypes.RANDOM:
new_brush_name.visible = true
new_brush_options.get_node("BrushName").visible = true
func add_brush() -> void:
@ -421,7 +459,7 @@ func add_brush() -> void:
Brushes.add_project_brush(image, file_name)
elif brush_type == BrushTypes.RANDOM:
var brush_name = new_brush_name.get_node("BrushNameLineEdit").text.to_lower()
var brush_name = new_brush_options.get_node("BrushName/BrushNameLineEdit").text.to_lower()
if !brush_name.is_valid_filename():
return
var dir := DirAccess.open(Global.home_data_directory.path_join("Brushes"))
@ -465,6 +503,7 @@ func _call_queue_redraw() -> void:
var empty_array: Array[Rect2i] = []
$"%SmartSlice".show_preview(empty_array)
$"%RowColumnLines".show_preview(1, 1)
await get_tree().process_frame
if (
current_import_option == ImageImportOptions.SPRITESHEET_TAB
or current_import_option == ImageImportOptions.SPRITESHEET_LAYER
@ -476,5 +515,5 @@ func _call_queue_redraw() -> void:
$"%RowColumnLines".show_preview(spritesheet_vertical, spritesheet_horizontal)
func _on_size_changed() -> void:
func _on_preview_container_size_changed() -> void:
_call_queue_redraw()

View file

@ -0,0 +1,255 @@
[gd_scene load_steps=6 format=3 uid="uid://nba3nryom3ud"]
[ext_resource type="Script" path="res://src/UI/Dialogs/ImportPreviewDialog.gd" id="1_r16hn"]
[ext_resource type="Script" path="res://src/UI/Dialogs/HelperScripts/RowColumnLines.gd" id="2_yokw4"]
[ext_resource type="Script" path="res://src/UI/Dialogs/HelperScripts/SmartSlicePreview.gd" id="3_aeccv"]
[ext_resource type="PackedScene" uid="uid://cii2hpylme60s" path="res://src/UI/Nodes/CollapsibleContainer.tscn" id="4_6ispq"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4_nmo33"]
[node name="ImportPreviewDialog" type="ConfirmationDialog"]
canvas_item_default_texture_filter = 0
position = Vector2i(0, 36)
size = Vector2i(316, 446)
script = ExtResource("1_r16hn")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -49.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/VBoxContainer/AspectRatioContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(300, 300)
layout_mode = 2
expand_mode = 1
stretch_mode = 5
[node name="RowColumnLines" type="Control" parent="VBoxContainer/VBoxContainer/AspectRatioContainer/TextureRect"]
unique_name_in_owner = true
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("2_yokw4")
[node name="SmartSlice" type="Control" parent="VBoxContainer/VBoxContainer/AspectRatioContainer/TextureRect"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_aeccv")
[node name="HSeparator" type="HSeparator" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="ApplyAll" type="CheckBox" parent="VBoxContainer/VBoxContainer"]
visible = false
layout_mode = 2
text = "Apply to all"
[node name="SizeContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 32
[node name="ImageSizeLabel" type="Label" parent="VBoxContainer/VBoxContainer/SizeContainer"]
layout_mode = 2
text = "Image Size: 64×64"
[node name="FrameSizeLabel" type="Label" parent="VBoxContainer/VBoxContainer/SizeContainer"]
visible = false
layout_mode = 2
text = "Frame size: 64×64"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
text = "Import as:"
[node name="ImportOption" type="OptionButton" parent="VBoxContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
mouse_default_cursor_shape = 2
[node name="ImportOptionsContainer" parent="VBoxContainer" instance=ExtResource("4_6ispq")]
layout_mode = 2
text = "Import Options"
[node name="ImportOptions" type="VBoxContainer" parent="VBoxContainer/ImportOptionsContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="SpritesheetOptions" type="VBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="Manual" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual"]
layout_mode = 2
text = "Horizontal frames:"
[node name="HorizontalFrames" type="SpinBox" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
[node name="Label2" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual"]
layout_mode = 2
text = "Vertical frames:"
[node name="VerticalFrames" type="SpinBox" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
[node name="Smart" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions"]
visible = false
layout_mode = 2
[node name="Threshold" type="TextureProgressBar" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart"]
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Images that have any one side smaller than this value will cross the threshold"
focus_mode = 2
theme_type_variation = &"ValueSlider"
value = 10.0
allow_greater = true
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_nmo33")
prefix = "Threshold:"
suffix = "px"
[node name="MergeDist" type="TextureProgressBar" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart"]
custom_minimum_size = Vector2(0, 30)
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Images which crossed the threshold will get merged into a larger image, if they are within this distance"
focus_mode = 2
theme_type_variation = &"ValueSlider"
value = 3.0
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_nmo33")
prefix = "Merge distance:"
suffix = "px"
[node name="Slice" type="Button" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart"]
layout_mode = 2
mouse_default_cursor_shape = 2
text = "Refresh"
[node name="SmartSliceToggle" type="CheckBox" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions"]
unique_name_in_owner = true
layout_mode = 2
mouse_default_cursor_shape = 2
text = "Smart Slice"
[node name="AtFrame" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions"]
visible = false
layout_mode = 2
[node name="Label3" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/AtFrame"]
layout_mode = 2
text = "At frame:"
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/AtFrame"]
layout_mode = 2
size_flags_vertical = 0
min_value = 1.0
value = 1.0
[node name="AtLayer" type="GridContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions"]
visible = false
layout_mode = 2
columns = 2
[node name="Label" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/AtLayer"]
layout_mode = 2
text = "At layer:"
[node name="AtLayerOption" type="OptionButton" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/AtLayer"]
layout_mode = 2
mouse_default_cursor_shape = 2
[node name="NewBrushOptions" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions"]
visible = false
layout_mode = 2
[node name="Type" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions/Type"]
layout_mode = 2
text = "Brush type:"
[node name="BrushTypeOption" type="OptionButton" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions/Type"]
layout_mode = 2
mouse_default_cursor_shape = 2
item_count = 3
selected = 0
popup/item_0/text = "File brush"
popup/item_0/id = 0
popup/item_1/text = "Project brush"
popup/item_1/id = 1
popup/item_2/text = "Random brush"
popup/item_2/id = 2
[node name="BrushName" type="HBoxContainer" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions/BrushName"]
layout_mode = 2
text = "Brush name:"
[node name="BrushNameLineEdit" type="LineEdit" parent="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions/BrushName"]
layout_mode = 2
[connection signal="about_to_popup" from="." to="." method="_on_ImportPreviewDialog_about_to_show"]
[connection signal="confirmed" from="." to="." method="_on_ImportPreviewDialog_confirmed"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="resized" from="VBoxContainer/VBoxContainer" to="." method="_on_preview_container_size_changed"]
[connection signal="toggled" from="VBoxContainer/VBoxContainer/ApplyAll" to="." method="_on_ApplyAll_toggled"]
[connection signal="item_selected" from="VBoxContainer/VBoxContainer/HBoxContainer/ImportOption" to="." method="_on_ImportOption_item_selected"]
[connection signal="value_changed" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual/HorizontalFrames" to="." method="_on_HorizontalFrames_value_changed"]
[connection signal="value_changed" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Manual/VerticalFrames" to="." method="_on_VerticalFrames_value_changed"]
[connection signal="value_changed" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart/Threshold" to="." method="_on_threshold_value_changed"]
[connection signal="value_changed" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart/MergeDist" to="." method="_on_merge_dist_value_changed"]
[connection signal="pressed" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/Smart/Slice" to="." method="_on_slice_pressed"]
[connection signal="toggled" from="VBoxContainer/ImportOptionsContainer/ImportOptions/SpritesheetOptions/SmartSliceToggle" to="." method="_on_smart_slice_toggled"]
[connection signal="item_selected" from="VBoxContainer/ImportOptionsContainer/ImportOptions/NewBrushOptions/Type/BrushTypeOption" to="." method="_on_BrushTypeOption_item_selected"]

View file

@ -1,266 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://nba3nryom3ud"]
[ext_resource type="Script" path="res://src/UI/Dialogs/PreviewDialog.gd" id="1"]
[ext_resource type="Script" path="res://src/UI/Dialogs/HelperScripts/RowColumnLines.gd" id="2_fni44"]
[ext_resource type="Script" path="res://src/UI/Dialogs/HelperScripts/SmartSlicePreview.gd" id="3_lsglt"]
[ext_resource type="Script" path="res://src/UI/Nodes/ValueSlider.gd" id="4_e0gk1"]
[node name="PreviewDialog" type="ConfirmationDialog"]
canvas_item_default_texture_filter = 0
size = Vector2i(316, 417)
script = ExtResource("1")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -49.0
[node name="AspectRatioContainer" type="AspectRatioContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="TextureRect" type="TextureRect" parent="VBoxContainer/AspectRatioContainer"]
custom_minimum_size = Vector2(300, 300)
layout_mode = 2
expand_mode = 1
stretch_mode = 5
[node name="RowColumnLines" type="Control" parent="VBoxContainer/AspectRatioContainer/TextureRect"]
unique_name_in_owner = true
anchors_preset = 0
offset_right = 40.0
offset_bottom = 40.0
script = ExtResource("2_fni44")
[node name="SmartSlice" type="Control" parent="VBoxContainer/AspectRatioContainer/TextureRect"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_lsglt")
[node name="ApplyAll" type="CheckBox" parent="VBoxContainer"]
visible = false
layout_mode = 2
text = "Apply to all"
[node name="SizeContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 32
[node name="ImageSizeLabel" type="Label" parent="VBoxContainer/SizeContainer"]
layout_mode = 2
text = "Image Size: 64×64"
[node name="FrameSizeLabel" type="Label" parent="VBoxContainer/SizeContainer"]
visible = false
layout_mode = 2
text = "Frame size: 64×64"
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
text = "Import as:"
[node name="ImportOption" type="OptionButton" parent="VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
mouse_default_cursor_shape = 2
[node name="SpritesheetTabOptions" type="GridContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="Manual" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions"]
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual"]
layout_mode = 2
text = "Horizontal frames:"
[node name="HorizontalFrames" type="SpinBox" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
[node name="Label2" type="Label" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual"]
layout_mode = 2
text = "Vertical frames:"
[node name="VerticalFrames" type="SpinBox" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
value = 1.0
[node name="SmartOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="Threshold" type="TextureProgressBar" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions"]
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Images that have any one side smaller than this value will cross the threshold"
focus_mode = 2
theme_type_variation = &"ValueSlider"
value = 10.0
allow_greater = true
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_e0gk1")
prefix = "Threshold:"
suffix = "px"
[node name="MergeDist" type="TextureProgressBar" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions"]
layout_mode = 2
size_flags_horizontal = 3
tooltip_text = "Images which crossed the threshold will get merged into a larger image, if they are within this distance"
focus_mode = 2
theme_type_variation = &"ValueSlider"
value = 3.0
nine_patch_stretch = true
stretch_margin_left = 3
stretch_margin_top = 3
stretch_margin_right = 3
stretch_margin_bottom = 3
script = ExtResource("4_e0gk1")
prefix = "Merge distance:"
suffix = "px"
[node name="Slice" type="Button" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
text = "Refresh"
[node name="SmartSliceButton" type="CheckBox" parent="VBoxContainer/HBoxContainer/SpritesheetTabOptions"]
unique_name_in_owner = true
layout_mode = 2
mouse_default_cursor_shape = 2
text = "Smart Slice"
[node name="SpritesheetLayerOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
[node name="Label3" type="Label" parent="VBoxContainer/HBoxContainer/SpritesheetLayerOptions"]
layout_mode = 2
size_flags_vertical = 0
text = "Start frame:"
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/SpritesheetLayerOptions"]
layout_mode = 2
size_flags_vertical = 0
min_value = 1.0
value = 1.0
[node name="NewFrameOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/NewFrameOptions"]
layout_mode = 2
text = "At layer:"
[node name="AtLayerOption" type="OptionButton" parent="VBoxContainer/HBoxContainer/NewFrameOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
[node name="ReplaceCelOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
layout_mode = 2
text = "At layer:"
[node name="AtLayerOption" type="OptionButton" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
[node name="Label2" type="Label" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
layout_mode = 2
text = "At frame:"
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/ReplaceCelOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
max_value = 1.0
value = 1.0
[node name="NewLayerOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/NewLayerOptions"]
layout_mode = 2
text = "At frame:"
[node name="AtFrameSpinbox" type="SpinBox" parent="VBoxContainer/HBoxContainer/NewLayerOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
min_value = 1.0
max_value = 1.0
value = 1.0
[node name="NewBrushOptions" type="HBoxContainer" parent="VBoxContainer/HBoxContainer"]
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/NewBrushOptions"]
layout_mode = 2
text = "Brush type:"
[node name="BrushTypeOption" type="OptionButton" parent="VBoxContainer/HBoxContainer/NewBrushOptions"]
layout_mode = 2
mouse_default_cursor_shape = 2
item_count = 3
selected = 0
popup/item_0/text = "File brush"
popup/item_0/id = 0
popup/item_1/text = "Project brush"
popup/item_1/id = 1
popup/item_2/text = "Random brush"
popup/item_2/id = 2
[node name="BrushName" type="HBoxContainer" parent="VBoxContainer/HBoxContainer/NewBrushOptions"]
visible = false
layout_mode = 2
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer/NewBrushOptions/BrushName"]
layout_mode = 2
text = "Brush name:"
[node name="BrushNameLineEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer/NewBrushOptions/BrushName"]
layout_mode = 2
[connection signal="about_to_popup" from="." to="." method="_on_PreviewDialog_about_to_show"]
[connection signal="confirmed" from="." to="." method="_on_PreviewDialog_confirmed"]
[connection signal="size_changed" from="." to="." method="_on_size_changed"]
[connection signal="visibility_changed" from="." to="." method="_on_visibility_changed"]
[connection signal="toggled" from="VBoxContainer/ApplyAll" to="." method="_on_ApplyAll_toggled"]
[connection signal="item_selected" from="VBoxContainer/HBoxContainer/ImportOption" to="." method="_on_ImportOption_item_selected"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual/HorizontalFrames" to="." method="_on_HorizontalFrames_value_changed"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/Manual/VerticalFrames" to="." method="_on_VerticalFrames_value_changed"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions/Threshold" to="." method="_on_threshold_value_changed"]
[connection signal="value_changed" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions/MergeDist" to="." method="_on_merge_dist_value_changed"]
[connection signal="pressed" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartOptions/Slice" to="." method="_on_slice_pressed"]
[connection signal="toggled" from="VBoxContainer/HBoxContainer/SpritesheetTabOptions/SmartSliceButton" to="." method="_on_smart_slice_toggled"]
[connection signal="item_selected" from="VBoxContainer/HBoxContainer/NewBrushOptions/BrushTypeOption" to="." method="_on_BrushTypeOption_item_selected"]