This commit is contained in:
2024-05-28 11:03:13 -05:00
commit 277fb03a69
75 changed files with 5206 additions and 0 deletions

View File

@@ -0,0 +1,45 @@
@tool
extends "../base_inspector_dock.gd"
var sprite_frames_creator = preload("../../../creators/sprite_frames/sprite_frames_creator.gd").new()
func _setup():
sprite_frames_creator.init(config)
func _get_available_layers(global_source_path: String) -> Array:
return sprite_frames_creator.list_layers(global_source_path)
func _get_available_slices(global_source_path: String) -> Array:
return sprite_frames_creator.list_slices(global_source_path)
func _do_import():
var root = get_tree().get_edited_scene_root()
var source_path = ProjectSettings.globalize_path(_source)
var options = {
"output_folder": _output_folder if _output_folder != "" else root.scene_file_path.get_base_dir(),
"exception_pattern": _ex_pattern_field.text,
"only_visible_layers": _visible_layers_field.button_pressed,
"output_filename": _out_filename_field.text,
"layer": _layer,
}
_save_config()
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
if not aseprite_output.is_ok:
var error = result_code.get_error_message(aseprite_output.code)
printerr(error)
_show_message(error)
return
file_system.scan()
await file_system.filesystem_changed
sprite_frames_creator.create_animations(target_node, aseprite_output.content, { "slice": _slice })
_handle_cleanup(aseprite_output.content)

View File

@@ -0,0 +1,12 @@
[gd_scene load_steps=3 format=3 uid="uid://vej7yqkbtd5f"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/animated_sprite/animated_sprite_inspector_dock.gd" id="1"]
[ext_resource type="PackedScene" uid="uid://uxm7b02wry10" path="res://addons/AsepriteWizard/interface/docks/dock_fields.tscn" id="2_2ilip"]
[node name="animated_sprite_inspector_dock" type="PanelContainer"]
offset_right = 14.0
offset_bottom = 14.0
script = ExtResource("1")
[node name="dock_fields" parent="." instance=ExtResource("2_2ilip")]
layout_mode = 2

View File

@@ -0,0 +1,18 @@
@tool
extends EditorInspectorPlugin
const ASInspectorDock = preload("./animated_sprite_inspector_dock.tscn")
var config
var file_system: EditorFileSystem
func _can_handle(object):
return object is AnimatedSprite2D || object is AnimatedSprite3D
func _parse_end(object):
var dock = ASInspectorDock.instantiate()
dock.target_node = object
dock.config = config
dock.file_system = file_system
add_custom_control(dock)

View File

@@ -0,0 +1,413 @@
@tool
extends PanelContainer
const wizard_config = preload("../../config/wizard_config.gd")
const result_code = preload("../../config/result_codes.gd")
var _aseprite_file_exporter = preload("../../aseprite/file_exporter.gd").new()
var scene: Node
var target_node: Node
var config
var file_system: EditorFileSystem
var _layer: String = ""
var _slice: String = ""
var _source: String = ""
var _file_dialog_aseprite: EditorFileDialog
var _output_folder_dialog: EditorFileDialog
var _importing := false
var _output_folder := ""
var _out_folder_default := "[Same as scene]"
var _layer_default := "[all]"
var _interface_section_state
@onready var _section_title := $dock_fields/VBoxContainer/title as Button
# general
@onready var _source_field := $dock_fields/VBoxContainer/source/button as Button
# layers
@onready var _layer_section_header := $dock_fields/VBoxContainer/extra/sections/layers/section_header as Button
@onready var _layer_section_container := $dock_fields/VBoxContainer/extra/sections/layers/section_content as MarginContainer
@onready var _layer_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/layer/options as OptionButton
@onready var _visible_layers_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/visible_layers/CheckBox as CheckBox
@onready var _ex_pattern_field := $dock_fields/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern/LineEdit as LineEdit
# slice
@onready var _slice_section_header := $dock_fields/VBoxContainer/extra/sections/slices/section_header as Button
@onready var _slice_section_container := $dock_fields/VBoxContainer/extra/sections/slices/section_content as MarginContainer
@onready var _slice_field := $dock_fields/VBoxContainer/extra/sections/slices/section_content/content/slice/options as OptionButton
# output
@onready var _output_section_header := $dock_fields/VBoxContainer/extra/sections/output/section_header as Button
@onready var _output_section_container := $dock_fields/VBoxContainer/extra/sections/output/section_content as MarginContainer
@onready var _out_folder_field := $dock_fields/VBoxContainer/extra/sections/output/section_content/content/out_folder/button as Button
@onready var _out_filename_field := $dock_fields/VBoxContainer/extra/sections/output/section_content/content/out_filename/LineEdit as LineEdit
@onready var _import_button := $dock_fields/VBoxContainer/import as Button
const INTERFACE_SECTION_KEY_LAYER = "layer_section"
const INTERFACE_SECTION_KEY_SLICE = "slice_section"
const INTERFACE_SECTION_KEY_OUTPUT = "output_section"
@onready var _expandable_sections = {
INTERFACE_SECTION_KEY_LAYER: { "header": _layer_section_header, "content": _layer_section_container},
INTERFACE_SECTION_KEY_SLICE: { "header": _slice_section_header, "content": _slice_section_container},
INTERFACE_SECTION_KEY_OUTPUT: { "header": _output_section_header, "content": _output_section_container},
}
func _ready():
_pre_setup()
_setup_interface()
_setup_config()
_aseprite_file_exporter.init(config)
_setup_field_listeners()
_setup()
func _setup_interface():
_hide_fields()
_show_specific_fields()
var cfg = wizard_config.load_interface_config(target_node)
_interface_section_state = cfg
_section_title.add_theme_stylebox_override("normal", _section_title.get_theme_stylebox("hover"))
for key in _expandable_sections:
_adjust_section_visibility(key)
func _setup_config():
var cfg = wizard_config.load_config(target_node)
if cfg == null:
_load_common_default_config()
else:
_load_common_config(cfg)
func _load_common_config(cfg):
if cfg.has("source"):
_set_source(cfg.source)
if cfg.get("layer", "") != "":
_layer_field.clear()
_set_layer(cfg.layer)
if cfg.get("slice", "") != "":
_slice_field.clear()
_set_slice(cfg.slice)
_set_out_folder(cfg.get("o_folder", ""))
_out_filename_field.text = cfg.get("o_name", "")
_visible_layers_field.button_pressed = cfg.get("only_visible", false)
_ex_pattern_field.text = cfg.get("o_ex_p", "")
_load_config(cfg)
func _load_common_default_config():
_ex_pattern_field.text = config.get_default_exclusion_pattern()
_visible_layers_field.button_pressed = config.should_include_only_visible_layers_by_default()
#_cleanup_hide_unused_nodes.button_pressed = config.is_set_visible_track_automatically_enabled()
_load_default_config()
func _set_source(source):
_source = source
_source_field.text = _source
_source_field.tooltip_text = _source
func _set_layer(layer):
_layer = layer
_layer_field.add_item(_layer)
func _set_slice(slice):
_slice = slice
_slice_field.add_item(_slice)
func _set_out_folder(path):
_output_folder = path
_out_folder_field.text = _output_folder if _output_folder != "" else _out_folder_default
_out_folder_field.tooltip_text = _out_folder_field.text
func _toggle_section_visibility(key: String) -> void:
_interface_section_state[key] = not _interface_section_state.get(key, false)
_adjust_section_visibility(key)
wizard_config.save_interface_config(target_node, _interface_section_state)
func _adjust_section_visibility(key: String) -> void:
var section = _expandable_sections[key]
var is_visible = _interface_section_state.get(key, false)
_adjust_icon(section.header, is_visible)
section.content.visible = is_visible
func _adjust_icon(section: Button, is_visible: bool = true) -> void:
var icon_name = "GuiTreeArrowDown" if is_visible else "GuiTreeArrowRight"
section.icon = get_theme_icon(icon_name, "EditorIcons")
func _setup_field_listeners():
_layer_section_header.button_down.connect(_on_layer_header_button_down)
_slice_section_header.button_down.connect(_on_slice_header_button_down)
_output_section_header.button_down.connect(_on_output_header_button_down)
_source_field.pressed.connect(_on_source_pressed)
_source_field.aseprite_file_dropped.connect(_on_source_aseprite_file_dropped)
_layer_field.button_down.connect(_on_layer_button_down)
_layer_field.item_selected.connect(_on_layer_item_selected)
_slice_field.button_down.connect(_on_slice_button_down)
_slice_field.item_selected.connect(_on_slice_item_selected)
_out_folder_field.dir_dropped.connect(_on_out_dir_dropped)
_out_folder_field.pressed.connect(_on_out_folder_pressed)
_import_button.pressed.connect(_on_import_pressed)
func _on_layer_header_button_down():
_toggle_section_visibility(INTERFACE_SECTION_KEY_LAYER)
func _on_slice_header_button_down():
_toggle_section_visibility(INTERFACE_SECTION_KEY_SLICE)
func _on_output_header_button_down():
_toggle_section_visibility(INTERFACE_SECTION_KEY_OUTPUT)
func _on_layer_button_down():
if _source == "":
_show_message("Please. Select source file first.")
return
var layers = _get_available_layers(ProjectSettings.globalize_path(_source))
_populate_options_field(_layer_field, layers, _layer)
func _on_layer_item_selected(index):
if index == 0:
_layer = ""
return
_layer = _layer_field.get_item_text(index)
_save_config()
func _on_slice_item_selected(index):
if index == 0:
_slice = ""
return
_slice = _slice_field.get_item_text(index)
_save_config()
func _on_slice_button_down():
if _source == "":
_show_message("Please, select source file first.")
return
var slices = _get_available_slices(ProjectSettings.globalize_path(_source))
var current = 0
_slice_field.clear()
_slice_field.add_item(_layer_default)
for s in slices:
if s == "":
continue
_slice_field.add_item(s)
if s == _slice:
current = _slice_field.get_item_count() - 1
_slice_field.select(current)
func _on_source_pressed():
_open_source_dialog()
##
## Save current import options to node metadata
##
func _save_config():
var child_config = _get_current_field_values()
var cfg := {
"source": _source,
"layer": _layer,
"slice": _slice,
"o_folder": _output_folder,
"o_name": _out_filename_field.text,
"only_visible": _visible_layers_field.button_pressed,
"o_ex_p": _ex_pattern_field.text,
}
for c in child_config:
cfg[c] = child_config[c]
wizard_config.save_config(target_node, config.is_use_metadata_enabled(), cfg)
func _get_import_options(default_folder: String):
return {
"output_folder": _output_folder if _output_folder != "" else default_folder,
"exception_pattern": _ex_pattern_field.text,
"only_visible_layers": _visible_layers_field.button_pressed,
"output_filename": _out_filename_field.text,
"layer": _layer
}
func _open_source_dialog():
_file_dialog_aseprite = _create_aseprite_file_selection()
get_parent().add_child(_file_dialog_aseprite)
if _source != "":
_file_dialog_aseprite.current_dir = ProjectSettings.globalize_path(_source.get_base_dir())
_file_dialog_aseprite.popup_centered_ratio()
func _create_aseprite_file_selection():
var file_dialog = EditorFileDialog.new()
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_FILE
file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
file_dialog.connect("file_selected",Callable(self,"_on_aseprite_file_selected"))
file_dialog.set_filters(PackedStringArray(["*.ase","*.aseprite"]))
return file_dialog
func _on_aseprite_file_selected(path):
_set_source(ProjectSettings.localize_path(path))
_save_config()
_file_dialog_aseprite.queue_free()
func _on_source_aseprite_file_dropped(path):
_set_source(path)
_save_config()
## Helper method to populate field with values
func _populate_options_field(field: OptionButton, values: Array, current_name: String):
var current = 0
field.clear()
field.add_item("[all]")
for v in values:
if v == "":
continue
field.add_item(v)
if v == current_name:
current = field.get_item_count() - 1
field.select(current)
func _on_out_folder_pressed():
_output_folder_dialog = _create_output_folder_selection()
get_parent().add_child(_output_folder_dialog)
if _output_folder != _out_folder_default:
_output_folder_dialog.current_dir = _output_folder
_output_folder_dialog.popup_centered_ratio()
func _create_output_folder_selection():
var file_dialog = EditorFileDialog.new()
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_DIR
file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
file_dialog.connect("dir_selected",Callable(self,"_on_output_folder_selected"))
return file_dialog
func _on_output_folder_selected(path):
_set_out_folder(path)
_output_folder_dialog.queue_free()
func _on_out_dir_dropped(path):
_set_out_folder(path)
func _show_message(message: String):
var _warning_dialog = AcceptDialog.new()
get_parent().add_child(_warning_dialog)
_warning_dialog.dialog_text = message
_warning_dialog.popup_centered()
_warning_dialog.connect("popup_hide",Callable(_warning_dialog,"queue_free"))
func _notify_aseprite_error(aseprite_error_code):
var error = result_code.get_error_message(aseprite_error_code)
printerr(error)
_show_message(error)
func _handle_cleanup(aseprite_content):
if config.should_remove_source_files():
DirAccess.remove_absolute(aseprite_content.data_file)
file_system.call_deferred("scan")
func _on_import_pressed():
if _importing:
return
_importing = true
if _source == "":
_show_message("Aseprite file not selected")
_importing = false
return
await _do_import()
_importing = false
# This is a little bit leaky as this base scene contains fields only relevant to animation players.
# However, this is the simplest thing I can do without overcomplicating stuff.
func _hide_fields():
$dock_fields/VBoxContainer/modes.hide()
$dock_fields/VBoxContainer/animation_player.hide()
$dock_fields/VBoxContainer/extra/sections/animation.hide()
## this will be called before base class does its setup
func _pre_setup():
pass
## this will be called after base class setup is complete
func _setup():
pass
func _load_default_config():
pass
func _load_config(cfg: Dictionary):
pass
## Override to return available layers
func _get_available_layers(global_source_path: String) -> Array:
return []
## Override to return available slices
func _get_available_slices(global_source_path: String) -> Array:
return []
## Override this method for extra import options to add to node metadata
func _get_current_field_values() -> Dictionary:
return {}
func _do_import():
pass
func _show_specific_fields() -> void:
pass

View File

@@ -0,0 +1,345 @@
[gd_scene load_steps=8 format=3 uid="uid://ljeu0l1ld6v5"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/base_inspector_dock.gd" id="1_0bpq8"]
[ext_resource type="PackedScene" uid="uid://x1f1t87m582u" path="res://addons/AsepriteWizard/interface/shared/animation_player_drop_button.tscn" id="2_pge1b"]
[ext_resource type="PackedScene" uid="uid://dj1uo3blocr8e" path="res://addons/AsepriteWizard/interface/shared/source_drop_button.tscn" id="3_nt1oj"]
[ext_resource type="PackedScene" uid="uid://cwvgnm3o7eed2" path="res://addons/AsepriteWizard/interface/shared/dir_drop_button.tscn" id="4_r7t2l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_x6usu"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.225, 0.225, 0.225, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="Image" id="Image_46k7x"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_dxtgh"]
image = SubResource("Image_46k7x")
[node name="base_inspector_dock" type="PanelContainer"]
offset_right = 14.0
offset_bottom = 14.0
script = ExtResource("1_0bpq8")
[node name="margin" type="MarginContainer" parent="."]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="margin"]
layout_mode = 2
[node name="title" type="Button" parent="margin/VBoxContainer"]
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_x6usu")
button_mask = 0
text = "Aseprite"
[node name="section_title" type="PanelContainer" parent="margin/VBoxContainer"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer" type="HBoxContainer" parent="margin/VBoxContainer/section_title"]
layout_mode = 2
alignment = 1
[node name="icon" type="TextureRect" parent="margin/VBoxContainer/section_title/HBoxContainer"]
custom_minimum_size = Vector2(16, 16)
layout_mode = 2
[node name="title" type="Label" parent="margin/VBoxContainer/section_title/HBoxContainer"]
layout_mode = 2
text = "Aseprite"
horizontal_alignment = 1
[node name="modes" type="HBoxContainer" parent="margin/VBoxContainer"]
layout_mode = 2
tooltip_text = "Import mode.
Animation mode (default): set spritesheet as texture and imports animations to the selected AnimationPlayer.
Image mode: Import only first frame and set as texture."
[node name="Label" type="Label" parent="margin/VBoxContainer/modes"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Mode"
[node name="options" type="OptionButton" parent="margin/VBoxContainer/modes"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 2
selected = 0
popup/item_0/text = "Animation"
popup/item_0/id = 0
popup/item_1/text = "Image"
popup/item_1/id = 1
[node name="animation_player" type="HBoxContainer" parent="margin/VBoxContainer"]
layout_mode = 2
tooltip_text = "AnimationPlayer node where animations should be added to."
[node name="Label" type="Label" parent="margin/VBoxContainer/animation_player"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "AnimationPlayer"
[node name="options" parent="margin/VBoxContainer/animation_player" instance=ExtResource("2_pge1b")]
layout_mode = 2
[node name="source" type="HBoxContainer" parent="margin/VBoxContainer"]
layout_mode = 2
tooltip_text = "Location of the Aseprite (*.ase, *.aseprite) source file."
[node name="Label" type="Label" parent="margin/VBoxContainer/source"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Aseprite File"
[node name="button" parent="margin/VBoxContainer/source" instance=ExtResource("3_nt1oj")]
layout_mode = 2
[node name="extra" type="MarginContainer" parent="margin/VBoxContainer"]
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="sections" type="VBoxContainer" parent="margin/VBoxContainer/extra"]
layout_mode = 2
[node name="layers" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/layers"]
layout_mode = 2
focus_mode = 0
text = "Layers"
icon = SubResource("ImageTexture_dxtgh")
alignment = 0
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/layers"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content"]
layout_mode = 2
[node name="layer" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "Aseprite layer to be used in the animation. By default all layers are included."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/layer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Layer"
[node name="options" type="OptionButton" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/layer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 1
selected = 0
popup/item_0/text = "[all]"
popup/item_0/id = 0
[node name="ex_pattern" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "Exclude layers with name matching this pattern (regex)."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Exclude Pattern"
[node name="LineEdit" type="LineEdit" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="visible_layers" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "If active, layers not visible in the source file won't be included in the final image."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Only Visible Layers"
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="slices" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/slices"]
layout_mode = 2
focus_mode = 0
text = "Slices"
icon = SubResource("ImageTexture_dxtgh")
alignment = 0
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/slices"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/slices/section_content"]
layout_mode = 2
[node name="slice" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/slices/section_content/content"]
layout_mode = 2
tooltip_text = "Aseprite slice to be used in the animation. By default, the whole file is included."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/slices/section_content/content/slice"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Slice"
[node name="options" type="OptionButton" parent="margin/VBoxContainer/extra/sections/slices/section_content/content/slice"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 1
selected = 0
popup/item_0/text = "[all]"
popup/item_0/id = 0
[node name="animation" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/animation"]
layout_mode = 2
focus_mode = 0
text = "Animation"
icon = SubResource("ImageTexture_dxtgh")
alignment = 0
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/animation"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content"]
layout_mode = 2
[node name="keep_length" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content/content"]
layout_mode = 2
tooltip_text = "When this is active the animation length won't be adjusted if other properties were added and the resulting imported animation is shorter."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Keep Manual Animation Length"
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="auto_visible_track" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/animation/section_content/content"]
layout_mode = 2
tooltip_text = "If active, it will automatically determine unused Sprite2D and Sprite3D nodes in each animation and hide them."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Hide Unused Sprites"
[node name="CheckBox" type="CheckBox" parent="margin/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="output" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="margin/VBoxContainer/extra/sections/output"]
layout_mode = 2
focus_mode = 0
text = "Output"
icon = SubResource("ImageTexture_dxtgh")
alignment = 0
[node name="section_content" type="MarginContainer" parent="margin/VBoxContainer/extra/sections/output"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content"]
layout_mode = 2
[node name="out_folder" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content/content"]
layout_mode = 2
tooltip_text = "Location where the spritesheet file should be saved."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Output Folder"
[node name="button" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder" instance=ExtResource("4_r7t2l")]
layout_mode = 2
[node name="out_filename" type="HBoxContainer" parent="margin/VBoxContainer/extra/sections/output/section_content/content"]
layout_mode = 2
tooltip_text = "Base filename for spritesheet. In case the layer option is used, this works as a prefix to the layer name."
[node name="Label" type="Label" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_filename"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Output File Name"
[node name="LineEdit" type="LineEdit" parent="margin/VBoxContainer/extra/sections/output/section_content/content/out_filename"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="import" type="Button" parent="margin/VBoxContainer"]
layout_mode = 2
text = "Import"
[connection signal="item_selected" from="margin/VBoxContainer/modes/options" to="." method="_on_modes_item_selected"]
[connection signal="button_down" from="margin/VBoxContainer/animation_player/options" to="." method="_on_options_button_down"]
[connection signal="item_selected" from="margin/VBoxContainer/animation_player/options" to="." method="_on_options_item_selected"]
[connection signal="node_dropped" from="margin/VBoxContainer/animation_player/options" to="." method="_on_animation_player_node_dropped"]
[connection signal="aseprite_file_dropped" from="margin/VBoxContainer/source/button" to="." method="_on_source_aseprite_file_dropped"]
[connection signal="pressed" from="margin/VBoxContainer/source/button" to="." method="_on_source_pressed"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/layers/section_header" to="." method="_on_layer_header_button_down"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/layers/section_content/content/layer/options" to="." method="_on_layer_button_down"]
[connection signal="item_selected" from="margin/VBoxContainer/extra/sections/layers/section_content/content/layer/options" to="." method="_on_layer_item_selected"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/slices/section_header" to="." method="_on_slice_header_button_down"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/slices/section_content/content/slice/options" to="." method="_on_slice_button_down"]
[connection signal="item_selected" from="margin/VBoxContainer/extra/sections/slices/section_content/content/slice/options" to="." method="_on_slice_item_selected"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/animation/section_header" to="." method="_on_animation_header_button_down"]
[connection signal="button_down" from="margin/VBoxContainer/extra/sections/output/section_header" to="." method="_on_output_header_button_down"]
[connection signal="dir_dropped" from="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder/button" to="." method="_on_out_dir_dropped"]
[connection signal="pressed" from="margin/VBoxContainer/extra/sections/output/section_content/content/out_folder/button" to="." method="_on_out_folder_pressed"]
[connection signal="pressed" from="margin/VBoxContainer/import" to="." method="_on_import_pressed"]

View File

@@ -0,0 +1,320 @@
[gd_scene load_steps=7 format=3 uid="uid://uxm7b02wry10"]
[ext_resource type="PackedScene" uid="uid://x1f1t87m582u" path="res://addons/AsepriteWizard/interface/shared/animation_player_drop_button.tscn" id="1_gh7xg"]
[ext_resource type="PackedScene" uid="uid://dj1uo3blocr8e" path="res://addons/AsepriteWizard/interface/shared/source_drop_button.tscn" id="1_y4qrm"]
[ext_resource type="PackedScene" uid="uid://cwvgnm3o7eed2" path="res://addons/AsepriteWizard/interface/shared/dir_drop_button.tscn" id="2_pypqg"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ptlkd"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(0.225, 0.225, 0.225, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="Image" id="Image_dv8o3"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_qqpl1"]
image = SubResource("Image_dv8o3")
[node name="dock_fields" type="MarginContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="title" type="Button" parent="VBoxContainer"]
layout_mode = 2
focus_mode = 0
theme_override_styles/normal = SubResource("StyleBoxFlat_ptlkd")
button_mask = 0
text = "Aseprite"
[node name="section_title" type="PanelContainer" parent="VBoxContainer"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/section_title"]
layout_mode = 2
alignment = 1
[node name="icon" type="TextureRect" parent="VBoxContainer/section_title/HBoxContainer"]
custom_minimum_size = Vector2(16, 16)
layout_mode = 2
[node name="title" type="Label" parent="VBoxContainer/section_title/HBoxContainer"]
layout_mode = 2
text = "Aseprite"
horizontal_alignment = 1
[node name="modes" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
tooltip_text = "Import mode.
Animation mode (default): set spritesheet as texture and imports animations to the selected AnimationPlayer.
Image mode: Import only first frame and set as texture."
[node name="Label" type="Label" parent="VBoxContainer/modes"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Mode"
[node name="options" type="OptionButton" parent="VBoxContainer/modes"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 2
selected = 0
popup/item_0/text = "Animation"
popup/item_0/id = 0
popup/item_1/text = "Image"
popup/item_1/id = 1
[node name="animation_player" type="HBoxContainer" parent="VBoxContainer" groups=["aw_animation_player_only"]]
layout_mode = 2
tooltip_text = "AnimationPlayer node where animations should be added to."
[node name="Label" type="Label" parent="VBoxContainer/animation_player"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "AnimationPlayer"
[node name="options" parent="VBoxContainer/animation_player" instance=ExtResource("1_gh7xg")]
layout_mode = 2
[node name="source" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
tooltip_text = "Location of the Aseprite (*.ase, *.aseprite) source file."
[node name="Label" type="Label" parent="VBoxContainer/source"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Aseprite File"
[node name="button" parent="VBoxContainer/source" instance=ExtResource("1_y4qrm")]
layout_mode = 2
[node name="extra" type="MarginContainer" parent="VBoxContainer"]
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="sections" type="VBoxContainer" parent="VBoxContainer/extra"]
layout_mode = 2
[node name="layers" type="VBoxContainer" parent="VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="VBoxContainer/extra/sections/layers"]
layout_mode = 2
focus_mode = 0
text = "Layers"
icon = SubResource("ImageTexture_qqpl1")
alignment = 0
[node name="section_content" type="MarginContainer" parent="VBoxContainer/extra/sections/layers"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="VBoxContainer/extra/sections/layers/section_content"]
layout_mode = 2
[node name="layer" type="HBoxContainer" parent="VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "Aseprite layer to be used in the animation. By default all layers are included."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/layers/section_content/content/layer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Layer"
[node name="options" type="OptionButton" parent="VBoxContainer/extra/sections/layers/section_content/content/layer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 1
selected = 0
popup/item_0/text = "[all]"
popup/item_0/id = 0
[node name="ex_pattern" type="HBoxContainer" parent="VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "Exclude layers with name matching this pattern (regex)."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Exclude Pattern"
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/extra/sections/layers/section_content/content/ex_pattern"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="visible_layers" type="HBoxContainer" parent="VBoxContainer/extra/sections/layers/section_content/content"]
layout_mode = 2
tooltip_text = "If active, layers not visible in the source file won't be included in the final image."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Only Visible Layers"
[node name="CheckBox" type="CheckBox" parent="VBoxContainer/extra/sections/layers/section_content/content/visible_layers"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="slices" type="VBoxContainer" parent="VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="VBoxContainer/extra/sections/slices"]
layout_mode = 2
focus_mode = 0
text = "Slices"
icon = SubResource("ImageTexture_qqpl1")
alignment = 0
[node name="section_content" type="MarginContainer" parent="VBoxContainer/extra/sections/slices"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="VBoxContainer/extra/sections/slices/section_content"]
layout_mode = 2
[node name="slice" type="HBoxContainer" parent="VBoxContainer/extra/sections/slices/section_content/content"]
layout_mode = 2
tooltip_text = "Aseprite slice to be used in the animation. By default, the whole file is included."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/slices/section_content/content/slice"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Slice"
[node name="options" type="OptionButton" parent="VBoxContainer/extra/sections/slices/section_content/content/slice"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
item_count = 1
selected = 0
popup/item_0/text = "[all]"
popup/item_0/id = 0
[node name="animation" type="VBoxContainer" parent="VBoxContainer/extra/sections" groups=["aw_animation_player_only"]]
layout_mode = 2
[node name="section_header" type="Button" parent="VBoxContainer/extra/sections/animation"]
layout_mode = 2
focus_mode = 0
text = "Animation"
icon = SubResource("ImageTexture_qqpl1")
alignment = 0
[node name="section_content" type="MarginContainer" parent="VBoxContainer/extra/sections/animation"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="VBoxContainer/extra/sections/animation/section_content"]
layout_mode = 2
[node name="keep_length" type="HBoxContainer" parent="VBoxContainer/extra/sections/animation/section_content/content"]
layout_mode = 2
tooltip_text = "When this is active the animation length won't be adjusted if other properties were added and the resulting imported animation is shorter."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Keep Manual Animation Length"
[node name="CheckBox" type="CheckBox" parent="VBoxContainer/extra/sections/animation/section_content/content/keep_length"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="auto_visible_track" type="HBoxContainer" parent="VBoxContainer/extra/sections/animation/section_content/content"]
layout_mode = 2
tooltip_text = "If active, it will automatically determine unused Sprite2D and Sprite3D nodes in each animation and hide them."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Hide Unused Sprites"
[node name="CheckBox" type="CheckBox" parent="VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "On"
[node name="output" type="VBoxContainer" parent="VBoxContainer/extra/sections"]
layout_mode = 2
[node name="section_header" type="Button" parent="VBoxContainer/extra/sections/output"]
layout_mode = 2
focus_mode = 0
text = "Output"
icon = SubResource("ImageTexture_qqpl1")
alignment = 0
[node name="section_content" type="MarginContainer" parent="VBoxContainer/extra/sections/output"]
visible = false
layout_mode = 2
theme_override_constants/margin_left = 10
[node name="content" type="VBoxContainer" parent="VBoxContainer/extra/sections/output/section_content"]
layout_mode = 2
[node name="out_folder" type="HBoxContainer" parent="VBoxContainer/extra/sections/output/section_content/content"]
layout_mode = 2
tooltip_text = "Location where the spritesheet file should be saved."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/output/section_content/content/out_folder"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Output Folder"
[node name="button" parent="VBoxContainer/extra/sections/output/section_content/content/out_folder" instance=ExtResource("2_pypqg")]
layout_mode = 2
[node name="out_filename" type="HBoxContainer" parent="VBoxContainer/extra/sections/output/section_content/content"]
layout_mode = 2
tooltip_text = "Base filename for spritesheet. In case the layer option is used, this works as a prefix to the layer name."
[node name="Label" type="Label" parent="VBoxContainer/extra/sections/output/section_content/content/out_filename"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Output File Name"
[node name="LineEdit" type="LineEdit" parent="VBoxContainer/extra/sections/output/section_content/content/out_filename"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="import" type="Button" parent="VBoxContainer"]
layout_mode = 2
text = "Import"

View File

@@ -0,0 +1,18 @@
@tool
extends EditorInspectorPlugin
const APInspectorDock = preload("./sprite_inspector_dock.tscn")
var config
var file_system: EditorFileSystem
func _can_handle(object):
return object is Sprite2D || object is Sprite3D || object is TextureRect
func _parse_end(object):
var dock = APInspectorDock.instantiate()
dock.target_node = object
dock.config = config
dock.file_system = file_system
add_custom_control(dock)

View File

@@ -0,0 +1,257 @@
@tool
extends "../base_inspector_dock.gd"
const AnimationCreator = preload("../../../creators/animation_player/animation_creator.gd")
const SpriteAnimationCreator = preload("../../../creators/animation_player/sprite_animation_creator.gd")
const TextureRectAnimationCreator = preload("../../../creators/animation_player/texture_rect_animation_creator.gd")
const StaticTextureCreator = preload("../../../creators/static_texture/texture_creator.gd")
enum ImportMode {
ANIMATION = 0,
IMAGE = 1
}
var animation_creator: AnimationCreator
var static_texture_creator: StaticTextureCreator
var _import_mode = -1
var _animation_player_path: String
@onready var _import_mode_options_field := $dock_fields/VBoxContainer/modes/options as OptionButton
@onready var _animation_player_field := $dock_fields/VBoxContainer/animation_player/options as OptionButton
@onready var _animation_player_container := $dock_fields/VBoxContainer/animation_player as HBoxContainer
# animation
@onready var _animation_section := $dock_fields/VBoxContainer/extra/sections/animation as VBoxContainer
@onready var _animation_section_header := $dock_fields/VBoxContainer/extra/sections/animation/section_header as Button
@onready var _animation_section_container := $dock_fields/VBoxContainer/extra/sections/animation/section_content as MarginContainer
@onready var _cleanup_hide_unused_nodes := $dock_fields/VBoxContainer/extra/sections/animation/section_content/content/auto_visible_track/CheckBox as CheckBox
@onready var _keep_length := $dock_fields/VBoxContainer/extra/sections/animation/section_content/content/keep_length/CheckBox as CheckBox
const INTERFACE_SECTION_KEY_ANIMATION = "animation_section"
func _pre_setup():
_expandable_sections[INTERFACE_SECTION_KEY_ANIMATION] = { "header": _animation_section_header, "content": _animation_section_container}
func _setup():
if target_node is Sprite2D || target_node is Sprite3D:
animation_creator = SpriteAnimationCreator.new()
if target_node is TextureRect:
animation_creator = TextureRectAnimationCreator.new()
static_texture_creator = StaticTextureCreator.new()
animation_creator.init(config)
static_texture_creator.init(config)
_setup_animation_fields_listeners()
func _load_config(cfg):
if cfg.has("player"):
_animation_player_field.clear()
_set_animation_player(cfg.player)
_cleanup_hide_unused_nodes.button_pressed = cfg.get("set_vis_track", config.is_set_visible_track_automatically_enabled())
_keep_length.button_pressed = cfg.get("keep_anim_length", false)
_set_import_mode(int(cfg.get("i_mode", 0)))
func _load_default_config():
_cleanup_hide_unused_nodes.button_pressed = config.is_set_visible_track_automatically_enabled()
func _set_animation_player(player):
_animation_player_path = player
_animation_player_field.add_item(_animation_player_path)
func _set_import_mode(import_mode):
if _import_mode == import_mode:
return
_import_mode = import_mode
var index = _import_mode_options_field.get_item_index(import_mode)
_import_mode_options_field.select(index)
_handle_import_mode()
func _handle_import_mode():
match _import_mode:
ImportMode.ANIMATION:
_animation_player_container.show()
_animation_section.show()
ImportMode.IMAGE:
_animation_player_container.hide()
_animation_section.hide()
func _setup_animation_fields_listeners():
_animation_section_header.button_down.connect(_on_animation_header_button_down)
_animation_player_field.node_dropped.connect(_on_animation_player_node_dropped)
_animation_player_field.button_down.connect(_on_animation_player_button_down)
_animation_player_field.item_selected.connect(_on_animation_player_item_selected)
_import_mode_options_field.item_selected.connect(_on_modes_item_selected)
func _on_animation_player_button_down():
_refresh_animation_players()
func _refresh_animation_players():
var animation_players = []
var root = get_tree().get_edited_scene_root()
_find_animation_players(root, root, animation_players)
var current = 0
_animation_player_field.clear()
_animation_player_field.add_item("[empty]")
for ap in animation_players:
_animation_player_field.add_item(ap)
if ap.get_concatenated_names() == _animation_player_path:
current = _animation_player_field.get_item_count() - 1
_animation_player_field.select(current)
func _find_animation_players(root: Node, node: Node, players: Array):
if node is AnimationPlayer:
players.push_back(root.get_path_to(node))
for c in node.get_children():
_find_animation_players(root, c, players)
func _on_animation_player_item_selected(index):
if index == 0:
_animation_player_path = ""
return
_animation_player_path = _animation_player_field.get_item_text(index)
_save_config()
func _do_import():
if _import_mode == ImportMode.IMAGE:
await _import_static()
return
await _import_for_animation_player()
##
## Import aseprite animations to target AnimationPlayer and set
## spritesheet as the node's texture
##
func _import_for_animation_player():
var root = get_tree().get_edited_scene_root()
if _animation_player_path == "" or not root.has_node(_animation_player_path):
_show_message("AnimationPlayer not found")
_importing = false
return
var source_path = ProjectSettings.globalize_path(_source)
var options = _get_import_options(root.scene_file_path.get_base_dir())
_save_config()
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
if not aseprite_output.is_ok:
_notify_aseprite_error(aseprite_output.code)
return
file_system.scan()
await file_system.filesystem_changed
var anim_options = {
"keep_anim_length": _keep_length.button_pressed,
"cleanup_hide_unused_nodes": _cleanup_hide_unused_nodes.button_pressed,
"slice": _slice,
}
animation_creator.create_animations(target_node, root.get_node(_animation_player_path), aseprite_output.content, anim_options)
_importing = false
_handle_cleanup(aseprite_output.content)
##
## Import first frame from aseprite file as node texture
##
func _import_static():
var source_path = ProjectSettings.globalize_path(_source)
var root = get_tree().get_edited_scene_root()
var options = _get_import_options(root.scene_file_path.get_base_dir())
options["first_frame_only"] = true
_save_config()
var aseprite_output = _aseprite_file_exporter.generate_aseprite_file(source_path, options)
if not aseprite_output.is_ok:
_notify_aseprite_error(aseprite_output.code)
return
file_system.scan()
await file_system.filesystem_changed
static_texture_creator.load_texture(target_node, aseprite_output.content, { "slice": _slice })
_importing = false
_handle_cleanup(aseprite_output.content)
func _get_current_field_values() -> Dictionary:
var cfg := {
"i_mode": _import_mode,
"player": _animation_player_path,
"keep_anim_length": _keep_length.button_pressed,
}
if _cleanup_hide_unused_nodes.button_pressed != config.is_set_visible_track_automatically_enabled():
cfg["set_vis_track"] = _cleanup_hide_unused_nodes.button_pressed
return cfg
func _get_available_layers(global_source_path: String) -> Array:
return animation_creator.list_layers(global_source_path)
func _get_available_slices(global_source_path: String) -> Array:
return animation_creator.list_slices(global_source_path)
func _on_animation_player_node_dropped(node_path):
var node = get_node(node_path)
var root = get_tree().get_edited_scene_root()
_animation_player_path = root.get_path_to(node)
for i in range(_animation_player_field.get_item_count()):
if _animation_player_field.get_item_text(i) == _animation_player_path:
_animation_player_field.select(i)
break
_save_config()
func _on_modes_item_selected(index):
var id = _import_mode_options_field.get_item_id(index)
_import_mode = id
_handle_import_mode()
func _on_animation_header_button_down():
_toggle_section_visibility(INTERFACE_SECTION_KEY_ANIMATION)
func _show_specific_fields():
_import_mode_options_field.get_parent().show()
_animation_player_container.show()
_animation_section.show()

View File

@@ -0,0 +1,12 @@
[gd_scene load_steps=3 format=3 uid="uid://biyshalfalqqw"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/sprite/sprite_inspector_dock.gd" id="1_ku6wj"]
[ext_resource type="PackedScene" uid="uid://uxm7b02wry10" path="res://addons/AsepriteWizard/interface/docks/dock_fields.tscn" id="2_wkqx4"]
[node name="sprite_inspector_dock" type="PanelContainer"]
offset_right = 14.0
offset_bottom = 14.0
script = ExtResource("1_ku6wj")
[node name="dock_fields" parent="." instance=ExtResource("2_wkqx4")]
layout_mode = 2

View File

@@ -0,0 +1,37 @@
@tool
extends TabContainer
signal close_requested
const WizardWindow = preload("./as_wizard_window.tscn")
var _config
var _file_system: EditorFileSystem
func _ready():
$Import.init(_config, _file_system)
$Import.connect("close_requested",Callable(self,"emit_signal").bind("close_requested"))
$Import.connect("import_success",Callable($History,"add_entry"))
$History.init(_config)
$History.connect("request_edit",Callable(self,"_on_edit_request"))
$History.connect("request_import",Callable(self,"_on_import_request"))
func init(config, editor_file_system: EditorFileSystem):
_config = config
_file_system = editor_file_system
func _on_AsWizardDockContainer_tab_changed(tab: int):
if tab == 1:
$History.reload()
func _on_edit_request(import_cfg: Dictionary):
$Import.load_import_config(import_cfg)
self.current_tab = 0
func _on_import_request(import_cfg: Dictionary):
$Import.load_import_config(import_cfg)
$Import.trigger_import()

View File

@@ -0,0 +1,26 @@
[gd_scene load_steps=4 format=3]
[ext_resource type="PackedScene" uid="uid://c5dwobjd34w3p" path="res://addons/AsepriteWizard/interface/docks/wizard/as_wizard_window.tscn" id="1"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/as_wizard_dock_container.gd" id="2"]
[ext_resource type="PackedScene" uid="uid://cyoin5ncul0fm" path="res://addons/AsepriteWizard/interface/docks/wizard/sprite_frames_import_history.tscn" id="3"]
[node name="AsWizardDockContainer" type="TabContainer"]
offset_right = 281.0
offset_bottom = 36.0
tab_alignment = 2
use_hidden_tabs_for_min_size = true
script = ExtResource( 2 )
[node name="Import" parent="." instance=ExtResource( 1 )]
anchor_right = 1.0
anchor_bottom = 1.0
offset_left = 4.0
offset_top = 32.0
offset_right = -4.0
offset_bottom = -4.0
tooltip_text = "SpriteFrames Importer"
[node name="History" parent="." instance=ExtResource( 3 )]
visible = false
[connection signal="tab_changed" from="." to="." method="_on_AsWizardDockContainer_tab_changed"]

View File

@@ -0,0 +1,222 @@
@tool
extends PanelContainer
signal close_requested
signal import_success(file_settings)
var result_code = preload("../../../config/result_codes.gd")
var _aseprite_file_exporter = preload("../../../aseprite/file_exporter.gd").new()
var _sf_creator = preload("../../../creators/sprite_frames/sprite_frames_creator.gd").new()
var _config
var _file_system: EditorFileSystem
var _file_dialog_aseprite: EditorFileDialog
var _output_folder_dialog: EditorFileDialog
var _warning_dialog: AcceptDialog
func _exit_tree():
_file_dialog_aseprite.queue_free()
_output_folder_dialog.queue_free()
_warning_dialog.queue_free()
func init(config, editor_file_system: EditorFileSystem):
_config = config
_file_system = editor_file_system
_file_dialog_aseprite = _create_aseprite_file_selection()
_output_folder_dialog = _create_outuput_folder_selection()
_warning_dialog = AcceptDialog.new()
_warning_dialog.exclusive = false
_sf_creator.init(_config)
_aseprite_file_exporter.init(config)
get_parent().get_parent().add_child(_file_dialog_aseprite)
get_parent().get_parent().add_child(_output_folder_dialog)
get_parent().get_parent().add_child(_warning_dialog)
_load_persisted_config()
func _load_persisted_config():
_split_mode_field().button_pressed = _config.should_split_layers()
_only_visible_layers_field().button_pressed = _config.should_include_only_visible_layers()
_exception_pattern_field().text = _config.get_exception_pattern()
_custom_name_field().text = _config.get_last_custom_name()
_file_location_field().text = _config.get_last_source_path()
_do_not_create_res_field().button_pressed = _config.should_not_create_resource()
var output_folder = _config.get_last_output_path()
_output_folder_field().text = output_folder if output_folder != "" else "res://"
func load_import_config(import_config: Dictionary):
_split_mode_field().button_pressed = import_config.options.export_mode == _aseprite_file_exporter.LAYERS_EXPORT_MODE
_only_visible_layers_field().button_pressed = import_config.options.only_visible_layers
_exception_pattern_field().text = import_config.options.exception_pattern
_custom_name_field().text = import_config.options.output_filename
_file_location_field().text = import_config.source_file
_do_not_create_res_field().button_pressed = import_config.options.do_not_create_resource
_output_folder_field().text = import_config.output_location if import_config.output_location != "" else "res://"
func _open_aseprite_file_selection_dialog():
var current_selection = _file_location_field().text
if current_selection != "":
_file_dialog_aseprite.current_dir = current_selection.get_base_dir()
_file_dialog_aseprite.popup_centered_ratio()
func _open_output_folder_selection_dialog():
var current_selection = _output_folder_field().text
if current_selection != "":
_output_folder_dialog.current_dir = current_selection
_output_folder_dialog.popup_centered_ratio()
func _create_aseprite_file_selection():
var file_dialog = EditorFileDialog.new()
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_FILE
file_dialog.access = EditorFileDialog.ACCESS_FILESYSTEM
file_dialog.connect("file_selected", _on_aseprite_file_selected)
file_dialog.set_filters(PackedStringArray(["*.ase","*.aseprite"]))
return file_dialog
func _create_outuput_folder_selection():
var file_dialog = EditorFileDialog.new()
file_dialog.file_mode = EditorFileDialog.FILE_MODE_OPEN_DIR
file_dialog.access = EditorFileDialog.ACCESS_RESOURCES
file_dialog.connect("dir_selected", _on_output_folder_selected)
return file_dialog
func _on_aseprite_file_selected(path):
var localized_path = ProjectSettings.localize_path(path)
_file_location_field().text = localized_path
_config.set_last_source_path(localized_path)
func _on_output_folder_selected(path):
_output_folder_field().text = path
_config.set_last_output_path(path)
func _on_next_btn_up():
var aseprite_file = _file_location_field().text
var output_location = _output_folder_field().text
var split_layers = _split_mode_field().button_pressed
var export_mode = _aseprite_file_exporter.LAYERS_EXPORT_MODE if split_layers else _aseprite_file_exporter.FILE_EXPORT_MODE
var options = {
"export_mode": export_mode,
"exception_pattern": _exception_pattern_field().text,
"only_visible_layers": _only_visible_layers_field().button_pressed,
"output_filename": _custom_name_field().text,
"do_not_create_resource": _do_not_create_res_field().button_pressed,
"output_folder": output_location,
}
var aseprite_output = _aseprite_file_exporter.generate_aseprite_files(
ProjectSettings.globalize_path(aseprite_file),
options
)
if not aseprite_output.is_ok:
_show_error(aseprite_output.code)
return
_file_system.scan()
await _file_system.filesystem_changed
var exit_code = OK
if !options.get("do_not_create_resource", false):
exit_code = _sf_creator.create_and_save_resources(aseprite_output.content)
if _config.should_remove_source_files():
_remove_source_files(aseprite_output.content)
if exit_code != OK:
_show_error(exit_code)
return
emit_signal("import_success", {
"source_file": aseprite_file,
"output_location": output_location,
"options": options,
})
_show_import_success_message()
func trigger_import():
_on_next_btn_up()
func _on_close_btn_up():
_close_window()
func _close_window():
_save_config()
self.emit_signal("close_requested")
func _save_config():
_config.set_split_layers(_split_mode_field().button_pressed)
_config.set_exception_pattern(_exception_pattern_field().text)
_config.set_custom_name(_custom_name_field().text)
_config.set_include_only_visible_layers(_only_visible_layers_field().button_pressed)
_config.set_do_not_create_resource(_do_not_create_res_field().button_pressed)
func _show_error(code: int):
_show_error_message(result_code.get_error_message(code))
func _show_error_message(message: String):
_warning_dialog.dialog_text = "Error: %s" % message
_warning_dialog.popup_centered()
func _show_import_success_message():
_warning_dialog.dialog_text = "Aseprite import succeeded"
_warning_dialog.popup_centered()
_save_config()
func _file_location_field() -> LineEdit:
return $container/options/file_location/HBoxContainer/file_location_path as LineEdit
func _output_folder_field() -> LineEdit:
return $container/options/output_folder/HBoxContainer/file_location_path as LineEdit
func _exception_pattern_field() -> LineEdit:
return $container/options/exclude_pattern/pattern as LineEdit
func _split_mode_field() -> CheckBox:
return $container/options/layer_importing_mode/split_layers/field as CheckBox
func _only_visible_layers_field() -> CheckBox:
return $container/options/layer_importing_mode/visible_layers/field as CheckBox
func _custom_name_field() -> LineEdit:
return $container/options/custom_filename/pattern as LineEdit
func _do_not_create_res_field() -> CheckBox:
return $container/options/layer_importing_mode/disable_resource_creation/field as CheckBox
func _remove_source_files(source_files: Array):
for s in source_files:
DirAccess.remove_absolute(s.data_file)
_file_system.call_deferred("scan")

View File

@@ -0,0 +1,139 @@
[gd_scene load_steps=2 format=3 uid="uid://c5dwobjd34w3p"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/as_wizard_window.gd" id="1"]
[node name="ASWizardWindow" type="PanelContainer"]
offset_right = 600.0
offset_bottom = 600.0
size_flags_horizontal = 3
size_flags_vertical = 0
script = ExtResource("1")
[node name="container" type="MarginContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="options" type="VBoxContainer" parent="container"]
layout_mode = 2
[node name="file_location" type="VBoxContainer" parent="container/options"]
layout_mode = 2
[node name="file_location_label" type="Label" parent="container/options/file_location"]
layout_mode = 2
tooltip_text = "Location of the Aseprite *.ase source file"
mouse_filter = 1
text = "Aseprite File Location:*"
clip_text = true
[node name="HBoxContainer" type="HBoxContainer" parent="container/options/file_location"]
layout_mode = 2
[node name="file_location_path" type="LineEdit" parent="container/options/file_location/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
caret_blink = true
[node name="file_location_btn" type="Button" parent="container/options/file_location/HBoxContainer"]
layout_mode = 2
text = "Select"
[node name="output_folder" type="VBoxContainer" parent="container/options"]
layout_mode = 2
[node name="label" type="Label" parent="container/options/output_folder"]
layout_mode = 2
tooltip_text = "Folder where the SpriteSheet resource is going to be saved"
mouse_filter = 1
text = "Output folder:*"
[node name="HBoxContainer" type="HBoxContainer" parent="container/options/output_folder"]
layout_mode = 2
[node name="file_location_path" type="LineEdit" parent="container/options/output_folder/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
text = "res://"
caret_blink = true
[node name="output_folder_btn" type="Button" parent="container/options/output_folder/HBoxContainer"]
layout_mode = 2
text = "Select"
[node name="exclude_pattern" type="VBoxContainer" parent="container/options"]
layout_mode = 2
[node name="label" type="Label" parent="container/options/exclude_pattern"]
layout_mode = 2
tooltip_text = "If layer name matches this pattern, it won't be exported."
mouse_filter = 1
text = "Exclude layers with name matching pattern:"
[node name="pattern" type="LineEdit" parent="container/options/exclude_pattern"]
layout_mode = 2
caret_blink = true
[node name="custom_filename" type="VBoxContainer" parent="container/options"]
layout_mode = 2
[node name="label" type="Label" parent="container/options/custom_filename"]
layout_mode = 2
tooltip_text = "Output filename. In case layers are not being merged, this is used as file prefix.
If not set, source filename is used."
mouse_filter = 1
text = "Output file name / prefix:"
[node name="pattern" type="LineEdit" parent="container/options/custom_filename"]
layout_mode = 2
caret_blink = true
[node name="layer_importing_mode" type="VBoxContainer" parent="container/options"]
layout_mode = 2
[node name="label" type="Label" parent="container/options/layer_importing_mode"]
layout_mode = 2
mouse_filter = 1
text = "Options:"
[node name="split_layers" type="HBoxContainer" parent="container/options/layer_importing_mode"]
layout_mode = 2
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/split_layers"]
layout_mode = 2
tooltip_text = "If selected, one resource will be created for each layer.
If not selected, layers will be merged and exported as one SpriteSheet."
text = "Split layers in multiple resources"
[node name="visible_layers" type="HBoxContainer" parent="container/options/layer_importing_mode"]
layout_mode = 2
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/visible_layers"]
layout_mode = 2
tooltip_text = "If selected, layers not visible in the source file won't be included in the final image."
text = "Only include visible layers"
[node name="disable_resource_creation" type="HBoxContainer" parent="container/options/layer_importing_mode"]
layout_mode = 2
[node name="field" type="CheckBox" parent="container/options/layer_importing_mode/disable_resource_creation"]
layout_mode = 2
tooltip_text = "Does not create SpriteFrames resource. Useful if you are only interested in the .json and .png output from Aseprite."
text = "Do not create resource file"
[node name="buttons" type="HBoxContainer" parent="container/options"]
layout_mode = 2
alignment = 2
[node name="close" type="Button" parent="container/options/buttons"]
layout_mode = 2
text = "Close"
[node name="next" type="Button" parent="container/options/buttons"]
layout_mode = 2
text = "Import"
[connection signal="button_up" from="container/options/file_location/HBoxContainer/file_location_btn" to="." method="_open_aseprite_file_selection_dialog"]
[connection signal="button_up" from="container/options/output_folder/HBoxContainer/output_folder_btn" to="." method="_open_output_folder_selection_dialog"]
[connection signal="button_up" from="container/options/buttons/close" to="." method="_on_close_btn_up"]
[connection signal="button_up" from="container/options/buttons/next" to="." method="_on_next_btn_up"]

View File

@@ -0,0 +1,226 @@
@tool
extends PanelContainer
signal request_edit(import_cfg)
signal request_import(import_cfg)
const SourcePathField = preload("./wizard_nodes/source_path.tscn")
const OutputPathField = preload("./wizard_nodes/output_path.tscn")
const ImportDateField = preload("./wizard_nodes/import_date.tscn")
const ItemActions = preload("./wizard_nodes/list_actions.tscn")
const SORT_BY_DATE := 0
const SORT_BY_PATH := 1
const INITIAL_GRID_INDEX := 3
var _config
var _history: Array
var _history_nodes := {}
var _history_nodes_list := []
var _is_busy := false
var _import_requested_for := -1
var _sort_by = SORT_BY_DATE
@onready var grid = $MarginContainer/VBoxContainer/ScrollContainer/GridContainer
@onready var loading_warning = $MarginContainer/VBoxContainer/loading_warning
@onready var no_history_warning = $MarginContainer/VBoxContainer/no_history_warning
func init(config):
_config = config
func reload():
if _history:
return
_history = _config.get_import_history()
for index in range(_history.size()):
var entry = _history[index]
_create_node_list_entry(entry, index)
loading_warning.hide()
if _history.is_empty():
no_history_warning.show()
else:
grid.get_parent().show()
func _create_node_list_entry(entry: Dictionary, index: int):
_add_to_node_list(entry, _create_nodes(entry, index))
func _create_nodes(entry: Dictionary, index: int) -> Dictionary:
var source_path = SourcePathField.instantiate()
source_path.set_entry(entry)
grid.get_child(INITIAL_GRID_INDEX).add_sibling(source_path)
var output_path = OutputPathField.instantiate()
output_path.text = entry.output_location
source_path.add_sibling(output_path)
var import_date = ImportDateField.instantiate()
import_date.set_date(entry.import_date)
output_path.add_sibling(import_date)
var actions = ItemActions.instantiate()
actions.history_index = index
import_date.add_sibling(actions)
actions.connect("import_clicked",Callable(self,"_on_entry_reimport_clicked"))
actions.connect("edit_clicked",Callable(self,"_on_entry_edit_clicked"))
actions.connect("removed_clicked",Callable(self,"_on_entry_remove_clicked"))
return {
"history_index": index,
"timestamp": entry.import_date,
"source_file": entry.source_file,
"source_path_node": source_path,
"output_path_node": output_path,
"import_date_node": import_date,
"actions_node": actions,
}
func _add_to_node_list(entry: Dictionary, node: Dictionary):
if not _history_nodes.has(entry.source_file):
_history_nodes[entry.source_file] = []
_history_nodes[entry.source_file].push_front(node)
_history_nodes_list.push_front(node)
func add_entry(file_settings: Dictionary):
if _history == null:
reload()
var dt = Time.get_datetime_dict_from_system()
file_settings["import_date"] = "%04d-%02d-%02d %02d:%02d:%02d" % [dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]
if _import_requested_for != -1:
_remove_item(_import_requested_for)
_import_requested_for = -1
elif _config.is_single_file_history() and _history_nodes.has(file_settings.source_file):
_remove_entries(file_settings.source_file)
_history.push_back(file_settings)
_config.save_import_history(_history)
_create_node_list_entry(file_settings, _history.size() - 1)
if _sort_by == SORT_BY_PATH:
_trigger_sort()
no_history_warning.hide()
loading_warning.hide()
grid.get_parent().show()
_is_busy = false
func _on_entry_reimport_clicked(entry_index: int):
if _is_busy:
return
_is_busy = true
_import_requested_for = entry_index
emit_signal("request_import", _history[entry_index])
func _on_entry_edit_clicked(entry_index: int):
if _is_busy:
return
emit_signal("request_edit", _history[entry_index])
func _on_entry_remove_clicked(entry_index: int):
if _is_busy:
return
_is_busy = true
_remove_item(entry_index)
_config.save_import_history(_history)
if (_history.is_empty()):
grid.get_parent().hide()
no_history_warning.show()
_is_busy = false
func _remove_item(entry_index: int):
var entry = _history[entry_index]
_remove_entries(entry.source_file, entry_index)
# removes nodes and entry from history. If entry_index is not provided, all
# entries for path are removed.
func _remove_entries(source_file_path: String, entry_index: int = -1):
var files_entries = _history_nodes[source_file_path]
var indexes_to_remove = []
for f in files_entries:
if entry_index == -1 or f.history_index == entry_index:
_free_entry_nodes(f)
_history_nodes_list.erase(f)
if entry_index != -1:
files_entries.erase(f)
_remove_from_history(f.history_index)
return
indexes_to_remove.push_back(f.history_index)
for i in indexes_to_remove:
_remove_from_history(i)
_history_nodes[source_file_path] = []
func _remove_from_history(entry_index: int):
var _already_adjusted = []
# to avoid re-creating the whole nodes list, I just decrement
# the index from items newer than the excluded one
for index in range(entry_index + 1, _history.size()):
if _already_adjusted.has(_history[index].source_file):
continue
_already_adjusted.push_back(_history[index].source_file)
var nodes = _history_nodes[_history[index].source_file]
for f in nodes:
if f.history_index > entry_index:
f.history_index -= 1
f.actions_node.history_index = f.history_index
_history.remove_at(entry_index)
func _free_entry_nodes(entry_history_node: Dictionary):
entry_history_node.source_path_node.queue_free()
entry_history_node.output_path_node.queue_free()
entry_history_node.import_date_node.queue_free()
entry_history_node.actions_node.queue_free()
func _on_SortOptions_item_selected(index):
if index == _sort_by:
return
_trigger_sort(index)
func _trigger_sort(sort_type: int = _sort_by):
if sort_type == SORT_BY_DATE:
_history_nodes_list.sort_custom(Callable(self,"_sort_by_date"))
else:
_history_nodes_list.sort_custom(Callable(self,"_sort_by_path"))
_reorganise_nodes()
_sort_by = sort_type
func _sort_by_date(a, b):
return a.timestamp < b.timestamp
func _sort_by_path(a, b):
return a.source_file > b.source_file
func _reorganise_nodes():
for entry in _history_nodes_list:
grid.move_child(entry.source_path_node, INITIAL_GRID_INDEX + 1)
grid.move_child(entry.output_path_node, INITIAL_GRID_INDEX + 2)
grid.move_child(entry.import_date_node, INITIAL_GRID_INDEX + 3)
grid.move_child(entry.actions_node, INITIAL_GRID_INDEX + 4)

View File

@@ -0,0 +1,81 @@
[gd_scene load_steps=2 format=3 uid="uid://cyoin5ncul0fm"]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/sprite_frames_import_history.gd" id="1"]
[node name="SpriteFramesImportHistory" type="PanelContainer"]
anchors_preset = 10
anchor_right = 1.0
grow_horizontal = 2
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource("1")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="list actions" type="HBoxContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
alignment = 2
[node name="divider" type="Label" parent="MarginContainer/VBoxContainer/list actions"]
layout_mode = 2
[node name="sort_label" type="Label" parent="MarginContainer/VBoxContainer/list actions"]
layout_mode = 2
text = "Sort by:"
[node name="SortOptions" type="OptionButton" parent="MarginContainer/VBoxContainer/list actions"]
layout_mode = 2
item_count = 2
selected = 0
popup/item_0/text = "Date"
popup/item_0/id = 0
popup/item_1/text = "Name"
popup/item_1/id = 1
[node name="loading_warning" type="Label" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 6
text = "Loading..."
[node name="no_history_warning" type="Label" parent="MarginContainer/VBoxContainer"]
visible = false
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 6
text = "No import history"
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
visible = false
layout_mode = 2
size_flags_vertical = 3
[node name="GridContainer" type="GridContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
columns = 4
[node name="source_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
text = "Source File"
[node name="output_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
layout_mode = 2
text = "Output Folder"
[node name="date_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
layout_mode = 2
text = "Date"
[node name="actions_label" type="Label" parent="MarginContainer/VBoxContainer/ScrollContainer/GridContainer"]
layout_mode = 2
text = "Actions"
[connection signal="item_selected" from="MarginContainer/VBoxContainer/list actions/SortOptions" to="." method="_on_SortOptions_item_selected"]

View File

@@ -0,0 +1,12 @@
@tool
extends Label
func set_date(timestamp: String):
self.text = timestamp
# self.text = _format_date(timestamp)
#func _format_date(timestamp: int) -> String:
# var dt = Time.get_datetime_dict_from_system_from_unix_time(timestamp)
# return "%04d-%02d-%02d %02d:%02d:%02d" % [dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second]

View File

@@ -0,0 +1,11 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/import_date.gd" id="1"]
[node name="import_date" type="Label"]
offset_left = 660.0
offset_top = 39.0
offset_right = 774.0
offset_bottom = 53.0
text = "2022-07-18 18:01"
script = ExtResource( 1 )

View File

@@ -0,0 +1,20 @@
@tool
extends HBoxContainer
signal import_clicked(index)
signal edit_clicked(index)
signal removed_clicked(index)
var history_index = -1
func _on_edit_pressed():
emit_signal("edit_clicked", history_index)
func _on_reimport_pressed():
emit_signal("import_clicked", history_index)
func _on_remove_pressed():
emit_signal("removed_clicked", history_index)

View File

@@ -0,0 +1,35 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/list_actions.gd" id="1"]
[node name="actions" type="HBoxContainer"]
offset_left = 784.0
offset_top = 34.0
offset_right = 950.0
offset_bottom = 58.0
custom_constants/separation = 5
script = ExtResource( 1 )
[node name="edit" type="Button" parent="."]
offset_right = 36.0
offset_bottom = 20.0
size_flags_vertical = 0
text = "Edit"
[node name="reimport" type="Button" parent="."]
offset_left = 41.0
offset_right = 97.0
offset_bottom = 20.0
size_flags_vertical = 0
text = "Import"
[node name="remove_at" type="Button" parent="."]
offset_left = 102.0
offset_right = 166.0
offset_bottom = 20.0
size_flags_vertical = 0
text = "Remove"
[connection signal="pressed" from="edit" to="." method="_on_edit_pressed"]
[connection signal="pressed" from="reimport" to="." method="_on_reimport_pressed"]
[connection signal="pressed" from="remove_at" to="." method="_on_remove_pressed"]

View File

@@ -0,0 +1,12 @@
[gd_scene format=2]
[node name="output_path" type="LineEdit"]
offset_left = 450.0
offset_top = 34.0
offset_right = 650.0
offset_bottom = 58.0
minimum_size = Vector2( 200, 0 )
size_flags_vertical = 0
text = "/some/output"
align = 3
editable = false

View File

@@ -0,0 +1,22 @@
@tool
extends LineEdit
func set_entry(entry: Dictionary):
self.text = entry.source_file
self.tooltip_text = _format_hint(entry)
func _format_hint(entry: Dictionary) -> String:
return """Output filename/prefix: %s
Ex. pattern: %s
Split: %s
Only visible: %s
No Resource: %s
""" % [
entry.options.output_filename,
entry.options.exception_pattern,
"yes" if entry.options.export_mode else "no",
"yes" if entry.options.only_visible_layers else "no",
"yes" if entry.options.do_not_create_resource else "no",
]

View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=3]
[ext_resource type="Script" path="res://addons/AsepriteWizard/interface/docks/wizard/wizard_nodes/source_path.gd" id="1"]
[node name="source_path" type="LineEdit"]
offset_top = 34.0
offset_right = 440.0
offset_bottom = 58.0
minimum_size = Vector2( 200, 0 )
tooltip_text = "Output name / prefix: some_name
Ex. Pattern: $_
Split: Yes
Only Visible: No
No Resource: No"
size_flags_vertical = 0
text = "/some/very/long/source/path/to/test/field"
editable = false
script = ExtResource( 1 )