gmtk-2024/addons/AsepriteWizard/interface/docks/base_inspector_dock.gd
2024-08-17 15:22:33 +01:00

429 lines
12 KiB
GDScript

@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 config = preload("../../config/config.gd").new()
var scene: Node
var target_node: Node
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
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()
_setup_field_listeners()
_setup()
_check_for_changes()
func _check_for_changes():
if not _source or _source == "":
return
var saved_hash = wizard_config.get_source_hash(target_node)
if saved_hash == "":
return
if saved_hash != FileAccess.get_md5(_source):
$dock_fields.show_source_change_warning()
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, 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
$dock_fields.hide_source_change_warning()
EditorInterface.save_scene()
# 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