You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
659 lines
24 KiB
659 lines
24 KiB
6 months ago
|
@tool
|
||
|
extends DialogicEditor
|
||
|
|
||
|
## Editor for editing character resources.
|
||
|
|
||
|
signal character_loaded(resource_path:String)
|
||
|
signal portrait_selected()
|
||
|
|
||
|
|
||
|
# Current state
|
||
|
var loading := false
|
||
|
var current_previewed_scene = null
|
||
|
|
||
|
# References
|
||
|
var selected_item: TreeItem
|
||
|
var def_portrait_path :String= DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn')
|
||
|
|
||
|
|
||
|
######### EDITOR STUFF and LOADING/SAVING ######################################
|
||
|
|
||
|
#region Resource Logic
|
||
|
## Method is called once editors manager is ready to accept registers.
|
||
|
func _register() -> void:
|
||
|
## Makes the editor open this when a .dch file is selected.
|
||
|
## Then _open_resource() is called.
|
||
|
editors_manager.register_resource_editor("dch", self)
|
||
|
|
||
|
## Add an "add character" button
|
||
|
var add_character_button = editors_manager.add_icon_button(
|
||
|
load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"),
|
||
|
'Add Character',
|
||
|
self)
|
||
|
add_character_button.pressed.connect(_on_create_character_button_pressed)
|
||
|
add_character_button.shortcut = Shortcut.new()
|
||
|
add_character_button.shortcut.events.append(InputEventKey.new())
|
||
|
add_character_button.shortcut.events[0].keycode = KEY_2
|
||
|
add_character_button.shortcut.events[0].ctrl_pressed = true
|
||
|
|
||
|
## By default show the no character screen
|
||
|
$NoCharacterScreen.show()
|
||
|
|
||
|
|
||
|
func _get_title() -> String:
|
||
|
return "Character"
|
||
|
|
||
|
|
||
|
func _get_icon() -> Texture:
|
||
|
return load("res://addons/dialogic/Editor/Images/Resources/character.svg")
|
||
|
|
||
|
|
||
|
## Called when a character is opened somehow
|
||
|
func _open_resource(resource:Resource) -> void:
|
||
|
if resource == null:
|
||
|
$NoCharacterScreen.show()
|
||
|
return
|
||
|
|
||
|
## Update resource
|
||
|
current_resource = (resource as DialogicCharacter)
|
||
|
|
||
|
## Make sure changes in the ui won't trigger saving
|
||
|
loading = true
|
||
|
|
||
|
## Load other main tabs
|
||
|
for child in %MainSettingsSections.get_children():
|
||
|
if child is DialogicCharacterEditorMainSection:
|
||
|
child._load_character(current_resource)
|
||
|
|
||
|
## Clear and then load Portrait section
|
||
|
%PortraitSearch.text = ""
|
||
|
load_portrait_tree()
|
||
|
|
||
|
loading = false
|
||
|
character_loaded.emit(resource.resource_path)
|
||
|
|
||
|
%CharacterName.text = DialogicResourceUtil.get_unique_identifier(resource.resource_path)
|
||
|
|
||
|
$NoCharacterScreen.hide()
|
||
|
%PortraitChangeInfo.hide()
|
||
|
|
||
|
|
||
|
## Called when the character is opened.
|
||
|
func _open(extra_info:Variant="") -> void:
|
||
|
if !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty():
|
||
|
def_portrait_path = ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')
|
||
|
else:
|
||
|
def_portrait_path = DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn')
|
||
|
|
||
|
if current_resource == null:
|
||
|
$NoCharacterScreen.show()
|
||
|
return
|
||
|
|
||
|
update_preview(true)
|
||
|
%PortraitChangeInfo.hide()
|
||
|
|
||
|
|
||
|
func _clear() -> void:
|
||
|
current_resource = null
|
||
|
current_resource_state = ResourceStates.SAVED
|
||
|
$NoCharacterScreen.show()
|
||
|
|
||
|
|
||
|
func _save() -> void:
|
||
|
if ! visible or not current_resource:
|
||
|
return
|
||
|
|
||
|
## Portrait list
|
||
|
current_resource.portraits = get_updated_portrait_dict()
|
||
|
|
||
|
## Main tabs
|
||
|
for child in %MainSettingsSections.get_children():
|
||
|
if child is DialogicCharacterEditorMainSection:
|
||
|
current_resource = child._save_changes(current_resource)
|
||
|
|
||
|
ResourceSaver.save(current_resource, current_resource.resource_path)
|
||
|
current_resource_state = ResourceStates.SAVED
|
||
|
DialogicResourceUtil.update_directory('dch')
|
||
|
|
||
|
|
||
|
## Saves a new empty character to the given path
|
||
|
func new_character(path: String) -> void:
|
||
|
var resource := DialogicCharacter.new()
|
||
|
resource.resource_path = path
|
||
|
resource.display_name = path.get_file().trim_suffix("."+path.get_extension())
|
||
|
resource.color = Color(1,1,1,1)
|
||
|
resource.default_portrait = ""
|
||
|
resource.custom_info = {}
|
||
|
ResourceSaver.save(resource, path)
|
||
|
DialogicResourceUtil.update_directory('dch')
|
||
|
editors_manager.edit_resource(resource)
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
|
||
|
######### INTERFACE ############################################################
|
||
|
|
||
|
#region Interface
|
||
|
func _ready() -> void:
|
||
|
if get_parent() is SubViewport:
|
||
|
return
|
||
|
|
||
|
DialogicUtil.get_dialogic_plugin().resource_saved.connect(_on_some_resource_saved)
|
||
|
# NOTE: This check is required because up to 4.2 this signal is not exposed.
|
||
|
if DialogicUtil.get_dialogic_plugin().has_signal("scene_saved"):
|
||
|
DialogicUtil.get_dialogic_plugin().scene_saved.connect(_on_some_resource_saved)
|
||
|
|
||
|
$NoCharacterScreen.color = get_theme_color("dark_color_2", "Editor")
|
||
|
$NoCharacterScreen.show()
|
||
|
setup_portrait_list_tab()
|
||
|
|
||
|
_on_fit_preview_toggle_toggled(DialogicUtil.get_editor_setting('character_preview_fit', true))
|
||
|
%PreviewLabel.add_theme_color_override("font_color", get_theme_color("readonly_color", "Editor"))
|
||
|
|
||
|
%PortraitChangeWarning.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
|
||
|
|
||
|
%RealPreviewPivot.texture = get_theme_icon("EditorPivot", "EditorIcons")
|
||
|
|
||
|
%MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
|
||
|
|
||
|
set_portrait_settings_position(DialogicUtil.get_editor_setting('portrait_settings_position', true))
|
||
|
|
||
|
await find_parent('EditorView').ready
|
||
|
|
||
|
## Add general tabs
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn").instantiate(), %MainSettingsSections)
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn").instantiate(), %MainSettingsSections)
|
||
|
|
||
|
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.tscn").instantiate(), %PortraitSettingsSection)
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn").instantiate(), %PortraitSettingsSection)
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn").instantiate(), %PortraitSettingsSection)
|
||
|
add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn").instantiate(), %PortraitSettingsSection)
|
||
|
|
||
|
## Load custom sections from modules
|
||
|
for indexer in DialogicUtil.get_indexers():
|
||
|
for path in indexer._get_character_editor_sections():
|
||
|
var scene: Control = load(path).instantiate()
|
||
|
if scene is DialogicCharacterEditorMainSection:
|
||
|
add_settings_section(scene, %MainSettingsSections)
|
||
|
elif scene is DialogicCharacterEditorPortraitSection:
|
||
|
add_settings_section(scene, %PortraitSettingsSection)
|
||
|
|
||
|
|
||
|
## Add a section (a control) either to the given settings section (Main or Portraits)
|
||
|
## - sets up the title of the section
|
||
|
## - connects to various signals
|
||
|
func add_settings_section(edit:Control, parent:Node) -> void:
|
||
|
edit.changed.connect(something_changed)
|
||
|
edit.character_editor = self
|
||
|
if edit.has_signal('update_preview'):
|
||
|
edit.update_preview.connect(update_preview)
|
||
|
|
||
|
var button :Button
|
||
|
if edit._show_title():
|
||
|
var hbox := HBoxContainer.new()
|
||
|
hbox.name = edit._get_title()+"BOX"
|
||
|
button = Button.new()
|
||
|
button.flat = true
|
||
|
button.theme_type_variation = "DialogicSection"
|
||
|
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||
|
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||
|
button.text = edit._get_title()
|
||
|
button.icon_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||
|
button.pressed.connect(_on_section_button_pressed.bind(button))
|
||
|
button.focus_mode = Control.FOCUS_NONE
|
||
|
button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons")
|
||
|
button.add_theme_color_override('icon_normal_color', get_theme_color("font_color", "DialogicSection"))
|
||
|
|
||
|
hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
|
hbox.add_child(button)
|
||
|
|
||
|
if !edit.hint_text.is_empty():
|
||
|
var hint :Node = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate()
|
||
|
hint.hint_text = edit.hint_text
|
||
|
hbox.add_child(hint)
|
||
|
|
||
|
parent.add_child(hbox)
|
||
|
parent.add_child(edit)
|
||
|
parent.add_child(HSeparator.new())
|
||
|
if button and !edit._start_opened():
|
||
|
_on_section_button_pressed(button)
|
||
|
|
||
|
|
||
|
func get_settings_section_by_name(name:String, main:=true) -> Node:
|
||
|
var parent := %MainSettingsSections
|
||
|
if not main:
|
||
|
parent = %PortraitSettingsSection
|
||
|
|
||
|
if parent.has_node(name):
|
||
|
return parent.get_node(name)
|
||
|
elif parent.has_node(name+"BOX/"+name):
|
||
|
return parent.get_node(name+"BOX/"+name)
|
||
|
else:
|
||
|
return null
|
||
|
|
||
|
|
||
|
func _on_section_button_pressed(button:Button) -> void:
|
||
|
var section_header := button.get_parent()
|
||
|
var section := section_header.get_parent().get_child(section_header.get_index()+1)
|
||
|
if section.visible:
|
||
|
button.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons")
|
||
|
section.visible = false
|
||
|
else:
|
||
|
button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons")
|
||
|
section.visible = true
|
||
|
|
||
|
if section_header.get_parent().get_child_count() > section_header.get_index()+2 and section_header.get_parent().get_child(section_header.get_index()+2) is Separator:
|
||
|
section_header.get_parent().get_child(section_header.get_index()+2).visible = section_header.get_parent().get_child(section_header.get_index()+1).visible
|
||
|
|
||
|
|
||
|
func something_changed(fake_argument = "", fake_arg2 = null) -> void:
|
||
|
if not loading:
|
||
|
current_resource_state = ResourceStates.UNSAVED
|
||
|
|
||
|
|
||
|
func _on_main_settings_collapse_toggled(button_pressed:bool) -> void:
|
||
|
%MainSettingsTitle.visible = !button_pressed
|
||
|
%MainSettingsScroll.visible = !button_pressed
|
||
|
if button_pressed:
|
||
|
%MainSettings.hide()
|
||
|
%MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
|
||
|
else:
|
||
|
%MainSettings.show()
|
||
|
%MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
|
||
|
|
||
|
|
||
|
func _on_switch_portrait_settings_position_pressed() -> void:
|
||
|
set_portrait_settings_position(!%RightSection.vertical)
|
||
|
|
||
|
|
||
|
func set_portrait_settings_position(is_below:bool) -> void:
|
||
|
%RightSection.vertical = is_below
|
||
|
DialogicUtil.set_editor_setting('portrait_settings_position', is_below)
|
||
|
if is_below:
|
||
|
%SwitchPortraitSettingsPosition.icon = get_theme_icon("ControlAlignRightWide", "EditorIcons")
|
||
|
else:
|
||
|
%SwitchPortraitSettingsPosition.icon = get_theme_icon("ControlAlignBottomWide", "EditorIcons")
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
|
||
|
########## PORTRAIT SECTION ####################################################
|
||
|
|
||
|
#region Portrait Section
|
||
|
func setup_portrait_list_tab() -> void:
|
||
|
%PortraitTree.editor = self
|
||
|
|
||
|
## Portrait section styling/connections
|
||
|
%AddPortraitButton.icon = get_theme_icon("Add", "EditorIcons")
|
||
|
%AddPortraitButton.pressed.connect(add_portrait)
|
||
|
%AddPortraitGroupButton.icon = load("res://addons/dialogic/Editor/Images/Pieces/add-folder.svg")
|
||
|
%AddPortraitGroupButton.pressed.connect(add_portrait_group)
|
||
|
%ImportPortraitsButton.icon = get_theme_icon("Load", "EditorIcons")
|
||
|
%ImportPortraitsButton.pressed.connect(open_portrait_folder_select)
|
||
|
%PortraitSearch.right_icon = get_theme_icon("Search", "EditorIcons")
|
||
|
%PortraitSearch.text_changed.connect(filter_portrait_list)
|
||
|
|
||
|
%PortraitTree.item_selected.connect(load_selected_portrait)
|
||
|
%PortraitTree.item_edited.connect(_on_item_edited)
|
||
|
%PortraitTree.item_activated.connect(_on_item_activated)
|
||
|
|
||
|
|
||
|
func open_portrait_folder_select() -> void:
|
||
|
find_parent("EditorView").godot_file_dialog(
|
||
|
import_portraits_from_folder, "*.svg, *.png",
|
||
|
EditorFileDialog.FILE_MODE_OPEN_DIR)
|
||
|
|
||
|
|
||
|
func import_portraits_from_folder(path:String) -> void:
|
||
|
var parent: TreeItem = %PortraitTree.get_root()
|
||
|
|
||
|
if %PortraitTree.get_selected() and %PortraitTree.get_selected() != parent and %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||
|
parent = %PortraitTree.get_selected()
|
||
|
|
||
|
var dir := DirAccess.open(path)
|
||
|
dir.list_dir_begin()
|
||
|
var file_name :String = dir.get_next()
|
||
|
while file_name != "":
|
||
|
if not dir.current_is_dir():
|
||
|
var file_lower = file_name.to_lower()
|
||
|
if '.svg' in file_lower or '.png' in file_lower:
|
||
|
if not '.import' in file_lower:
|
||
|
var final_name: String = path.path_join(file_name)
|
||
|
%PortraitTree.add_portrait_item(file_name.trim_suffix('.'+file_name.get_extension()),
|
||
|
{'scene':"",'export_overrides':{'image':var_to_str(final_name)}, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent)
|
||
|
file_name = dir.get_next()
|
||
|
|
||
|
## Handle selection
|
||
|
if parent.get_child_count():
|
||
|
parent.get_first_child().select(0)
|
||
|
else:
|
||
|
# Call anyways to clear preview and hide portrait settings section
|
||
|
load_selected_portrait()
|
||
|
|
||
|
something_changed()
|
||
|
|
||
|
|
||
|
func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary={'scene':"", 'export_overrides':{'image':''}, 'scale':1, 'offset':Vector2(), 'mirror':false}) -> void:
|
||
|
var parent: TreeItem = %PortraitTree.get_root()
|
||
|
if %PortraitTree.get_selected():
|
||
|
if %PortraitTree.get_selected().get_metadata(0) and %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||
|
parent = %PortraitTree.get_selected()
|
||
|
else:
|
||
|
parent = %PortraitTree.get_selected().get_parent()
|
||
|
var item: TreeItem = %PortraitTree.add_portrait_item(portrait_name, portrait_data, parent)
|
||
|
item.set_meta('new', true)
|
||
|
item.set_editable(0, true)
|
||
|
item.select(0)
|
||
|
%PortraitTree.call_deferred('edit_selected')
|
||
|
something_changed()
|
||
|
|
||
|
|
||
|
func add_portrait_group() -> void:
|
||
|
var parent_item: TreeItem = %PortraitTree.get_root()
|
||
|
if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
|
||
|
parent_item = %PortraitTree.get_selected()
|
||
|
var item :TreeItem = %PortraitTree.add_portrait_group("Group", parent_item)
|
||
|
item.set_meta('new', true)
|
||
|
item.set_editable(0, true)
|
||
|
item.select(0)
|
||
|
%PortraitTree.call_deferred('edit_selected')
|
||
|
|
||
|
|
||
|
func load_portrait_tree() -> void:
|
||
|
%PortraitTree.clear_tree()
|
||
|
var root: TreeItem = %PortraitTree.create_item()
|
||
|
|
||
|
for portrait in current_resource.portraits.keys():
|
||
|
var portrait_label = portrait
|
||
|
var parent = %PortraitTree.get_root()
|
||
|
if '/' in portrait:
|
||
|
parent = %PortraitTree.create_necessary_group_items(portrait)
|
||
|
portrait_label = portrait.split('/')[-1]
|
||
|
|
||
|
%PortraitTree.add_portrait_item(portrait_label, current_resource.portraits[portrait], parent)
|
||
|
|
||
|
update_default_portrait_star(current_resource.default_portrait)
|
||
|
|
||
|
if root.get_child_count():
|
||
|
root.get_first_child().select(0)
|
||
|
while %PortraitTree.get_selected().get_child_count():
|
||
|
%PortraitTree.get_selected().get_child(0).select(0)
|
||
|
else:
|
||
|
# Call anyways to clear preview and hide portrait settings section
|
||
|
load_selected_portrait()
|
||
|
|
||
|
|
||
|
func filter_portrait_list(filter_term := "") -> void:
|
||
|
filter_branch(%PortraitTree.get_root(), filter_term)
|
||
|
|
||
|
|
||
|
func filter_branch(parent: TreeItem, filter_term: String) -> bool:
|
||
|
var anything_visible := false
|
||
|
for item in parent.get_children():
|
||
|
if item.get_metadata(0).has('group'):
|
||
|
item.visible = filter_branch(item, filter_term)
|
||
|
anything_visible = item.visible
|
||
|
elif filter_term.is_empty() or filter_term.to_lower() in item.get_text(0).to_lower():
|
||
|
item.visible = true
|
||
|
anything_visible = true
|
||
|
else:
|
||
|
item.visible = false
|
||
|
return anything_visible
|
||
|
|
||
|
|
||
|
## This is used to save the portrait data
|
||
|
func get_updated_portrait_dict() -> Dictionary:
|
||
|
return list_portraits(%PortraitTree.get_root().get_children())
|
||
|
|
||
|
|
||
|
func list_portraits(tree_items: Array[TreeItem], dict := {}, path_prefix := "") -> Dictionary:
|
||
|
for item in tree_items:
|
||
|
if item.get_metadata(0).has('group'):
|
||
|
dict = list_portraits(item.get_children(), dict, path_prefix+item.get_text(0)+"/")
|
||
|
else:
|
||
|
dict[path_prefix +item.get_text(0)] = item.get_metadata(0)
|
||
|
return dict
|
||
|
|
||
|
|
||
|
func load_selected_portrait() -> void:
|
||
|
if selected_item and is_instance_valid(selected_item):
|
||
|
selected_item.set_editable(0, false)
|
||
|
|
||
|
selected_item = %PortraitTree.get_selected()
|
||
|
|
||
|
if selected_item and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'):
|
||
|
%PortraitSettingsSection.show()
|
||
|
var current_portrait_data: Dictionary = selected_item.get_metadata(0)
|
||
|
portrait_selected.emit(%PortraitTree.get_full_item_name(selected_item), current_portrait_data)
|
||
|
|
||
|
update_preview()
|
||
|
|
||
|
for child in %PortraitSettingsSection.get_children():
|
||
|
if child is DialogicCharacterEditorPortraitSection:
|
||
|
child.selected_item = selected_item
|
||
|
child._load_portrait_data(current_portrait_data)
|
||
|
|
||
|
else:
|
||
|
%PortraitSettingsSection.hide()
|
||
|
update_preview()
|
||
|
|
||
|
|
||
|
func delete_portrait_item(item: TreeItem) -> void:
|
||
|
if item.get_next_visible(true) and item.get_next_visible(true) != item:
|
||
|
item.get_next_visible(true).select(0)
|
||
|
else:
|
||
|
selected_item = null
|
||
|
load_selected_portrait()
|
||
|
item.free()
|
||
|
something_changed()
|
||
|
|
||
|
|
||
|
func duplicate_item(item: TreeItem) -> void:
|
||
|
var new_item: TreeItem = %PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0).duplicate(true), item.get_parent())
|
||
|
new_item.set_meta('new', true)
|
||
|
new_item.select(0)
|
||
|
|
||
|
|
||
|
func _input(event: InputEvent) -> void:
|
||
|
if !is_visible_in_tree() or (get_viewport().gui_get_focus_owner()!= null and !name+'/' in str(get_viewport().gui_get_focus_owner().get_path())):
|
||
|
return
|
||
|
if event is InputEventKey and event.pressed:
|
||
|
if event.keycode == KEY_F2 and %PortraitTree.get_selected():
|
||
|
%PortraitTree.get_selected().set_editable(0, true)
|
||
|
%PortraitTree.edit_selected()
|
||
|
get_viewport().set_input_as_handled()
|
||
|
elif event.keycode == KEY_DELETE and get_viewport().gui_get_focus_owner() is Tree and %PortraitTree.get_selected():
|
||
|
delete_portrait_item(%PortraitTree.get_selected())
|
||
|
get_viewport().set_input_as_handled()
|
||
|
|
||
|
|
||
|
func _on_portrait_right_click_menu_index_pressed(id: int) -> void:
|
||
|
# RENAME BUTTON
|
||
|
if id == 0:
|
||
|
_on_item_activated()
|
||
|
# DELETE BUTTON
|
||
|
if id == 2:
|
||
|
delete_portrait_item(%PortraitTree.get_selected())
|
||
|
# DUPLICATE ITEM
|
||
|
elif id == 1:
|
||
|
duplicate_item(%PortraitTree.get_selected())
|
||
|
elif id == 4:
|
||
|
get_settings_section_by_name("Portraits").set_default_portrait(%PortraitTree.get_full_item_name(%PortraitTree.get_selected()))
|
||
|
|
||
|
|
||
|
## This removes/and adds the DEFAULT star on the portrait list
|
||
|
func update_default_portrait_star(default_portrait_name: String) -> void:
|
||
|
var item_list: Array = %PortraitTree.get_root().get_children()
|
||
|
if item_list.is_empty() == false:
|
||
|
while true:
|
||
|
var item := item_list.pop_back()
|
||
|
if item.get_button_by_id(0, 2) != -1:
|
||
|
item.erase_button(0, item.get_button_by_id(0, 2))
|
||
|
if %PortraitTree.get_full_item_name(item) == default_portrait_name:
|
||
|
item.add_button(0, get_theme_icon("Favorites", "EditorIcons"), 2, true, "Default")
|
||
|
item_list.append_array(item.get_children())
|
||
|
if item_list.is_empty():
|
||
|
break
|
||
|
|
||
|
|
||
|
func _on_item_edited() -> void:
|
||
|
selected_item = %PortraitTree.get_selected()
|
||
|
something_changed()
|
||
|
if selected_item:
|
||
|
if %PreviewLabel.text.trim_prefix('Preview of "').trim_suffix('"') == current_resource.default_portrait:
|
||
|
current_resource.default_portrait = %PortraitTree.get_full_item_name(selected_item)
|
||
|
selected_item.set_editable(0, false)
|
||
|
|
||
|
if !selected_item.has_meta('new') and %PortraitTree.get_full_item_name(selected_item) != selected_item.get_meta('previous_name'):
|
||
|
report_name_change(selected_item)
|
||
|
%PortraitChangeInfo.show()
|
||
|
update_preview()
|
||
|
|
||
|
|
||
|
func _on_item_activated() -> void:
|
||
|
if %PortraitTree.get_selected() == null:
|
||
|
return
|
||
|
%PortraitTree.get_selected().set_editable(0, true)
|
||
|
%PortraitTree.edit_selected()
|
||
|
|
||
|
|
||
|
func report_name_change(item: TreeItem) -> void:
|
||
|
if item.get_metadata(0).has('group'):
|
||
|
for s_item in item.get_children():
|
||
|
if s_item.get_metadata(0).has('group') or !s_item.has_meta('new'):
|
||
|
report_name_change(s_item)
|
||
|
else:
|
||
|
if item.get_meta('previous_name') == %PortraitTree.get_full_item_name(item):
|
||
|
return
|
||
|
editors_manager.reference_manager.add_portrait_ref_change(
|
||
|
item.get_meta('previous_name'),
|
||
|
%PortraitTree.get_full_item_name(item),
|
||
|
[DialogicResourceUtil.get_unique_identifier(current_resource.resource_path)])
|
||
|
item.set_meta('previous_name', %PortraitTree.get_full_item_name(item))
|
||
|
%PortraitChangeInfo.show()
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
########### PREVIEW ############################################################
|
||
|
|
||
|
#region Preview
|
||
|
func update_preview(force := false) -> void:
|
||
|
%ScenePreviewWarning.hide()
|
||
|
if selected_item and is_instance_valid(selected_item) and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'):
|
||
|
%PreviewLabel.text = 'Preview of "'+%PortraitTree.get_full_item_name(selected_item)+'"'
|
||
|
|
||
|
var current_portrait_data: Dictionary = selected_item.get_metadata(0)
|
||
|
|
||
|
if not force and current_previewed_scene != null \
|
||
|
and current_previewed_scene.get_meta('path', '') == current_portrait_data.get('scene') \
|
||
|
and current_previewed_scene.has_method('_should_do_portrait_update') \
|
||
|
and is_instance_valid(current_previewed_scene.get_script()) \
|
||
|
and current_previewed_scene._should_do_portrait_update(current_resource, selected_item.get_text(0)):
|
||
|
pass # we keep the same scene
|
||
|
else:
|
||
|
for node in %RealPreviewPivot.get_children():
|
||
|
node.queue_free()
|
||
|
|
||
|
current_previewed_scene = null
|
||
|
|
||
|
var scene_path := def_portrait_path
|
||
|
if not current_portrait_data.get('scene', '').is_empty():
|
||
|
scene_path = current_portrait_data.get('scene')
|
||
|
|
||
|
if ResourceLoader.exists(scene_path):
|
||
|
current_previewed_scene = load(scene_path).instantiate()
|
||
|
|
||
|
if current_previewed_scene:
|
||
|
%RealPreviewPivot.add_child(current_previewed_scene)
|
||
|
|
||
|
if current_previewed_scene != null:
|
||
|
var scene: Node = current_previewed_scene
|
||
|
|
||
|
scene.show_behind_parent = true
|
||
|
DialogicUtil.apply_scene_export_overrides(scene, current_portrait_data.get('export_overrides', {}))
|
||
|
|
||
|
var mirror: bool = current_portrait_data.get('mirror', false) != current_resource.mirror
|
||
|
var scale: float = current_portrait_data.get('scale', 1) * current_resource.scale
|
||
|
if current_portrait_data.get('ignore_char_scale', false):
|
||
|
scale = current_portrait_data.get('scale', 1)
|
||
|
var offset: Vector2 = current_portrait_data.get('offset', Vector2()) + current_resource.offset
|
||
|
|
||
|
if is_instance_valid(scene.get_script()) and scene.script.is_tool():
|
||
|
if scene.has_method('_update_portrait'):
|
||
|
## Create a fake duplicate resource that has all the portrait changes applied already
|
||
|
var preview_character := current_resource.duplicate()
|
||
|
preview_character.portraits = get_updated_portrait_dict()
|
||
|
scene._update_portrait(preview_character, %PortraitTree.get_full_item_name(selected_item))
|
||
|
if scene.has_method('_set_mirror'):
|
||
|
scene._set_mirror(mirror)
|
||
|
|
||
|
if !%FitPreview_Toggle.button_pressed:
|
||
|
scene.position = Vector2() + offset
|
||
|
scene.scale = Vector2(1,1)*scale
|
||
|
else:
|
||
|
if is_instance_valid(scene.get_script()) and scene.script.is_tool() and scene.has_method('_get_covered_rect'):
|
||
|
var rect: Rect2= scene._get_covered_rect()
|
||
|
var available_rect: Rect2 = %FullPreviewAvailableRect.get_rect()
|
||
|
scene.scale = Vector2(1,1) * min(available_rect.size.x/rect.size.x, available_rect.size.y/rect.size.y)
|
||
|
%RealPreviewPivot.position = (rect.position)*-1*scene.scale
|
||
|
%RealPreviewPivot.position.x = %FullPreviewAvailableRect.size.x/2
|
||
|
scene.position = Vector2()
|
||
|
else:
|
||
|
%ScenePreviewWarning.show()
|
||
|
else:
|
||
|
%PreviewLabel.text = 'Nothing to preview'
|
||
|
for child in %PortraitSettingsSection.get_children():
|
||
|
if child is DialogicCharacterEditorPortraitSection:
|
||
|
child._recheck(current_portrait_data)
|
||
|
else:
|
||
|
%PreviewLabel.text = 'No portrait to preview.'
|
||
|
for node in %RealPreviewPivot.get_children():
|
||
|
node.queue_free()
|
||
|
current_previewed_scene = null
|
||
|
|
||
|
|
||
|
func _on_some_resource_saved(file:Variant) -> void:
|
||
|
if current_previewed_scene == null:
|
||
|
return
|
||
|
|
||
|
if file is Resource and file == current_previewed_scene.script:
|
||
|
update_preview(true)
|
||
|
|
||
|
if typeof(file) == TYPE_STRING and file == current_previewed_scene.get_meta("path", ""):
|
||
|
update_preview(true)
|
||
|
|
||
|
|
||
|
func _on_full_preview_available_rect_resized():
|
||
|
if %FitPreview_Toggle.button_pressed:
|
||
|
update_preview()
|
||
|
|
||
|
|
||
|
func _on_create_character_button_pressed():
|
||
|
editors_manager.show_add_resource_dialog(
|
||
|
new_character,
|
||
|
'*.dch; DialogicCharacter',
|
||
|
'Create new character',
|
||
|
'character',
|
||
|
)
|
||
|
|
||
|
|
||
|
func _on_fit_preview_toggle_toggled(button_pressed):
|
||
|
%FitPreview_Toggle.set_pressed_no_signal(button_pressed)
|
||
|
if button_pressed:
|
||
|
%FitPreview_Toggle.icon = get_theme_icon("ScrollContainer", "EditorIcons")
|
||
|
%FitPreview_Toggle.tooltip_text = "Real scale"
|
||
|
else:
|
||
|
%FitPreview_Toggle.tooltip_text = "Fit into preview"
|
||
|
%FitPreview_Toggle.icon = get_theme_icon("CenterContainer", "EditorIcons")
|
||
|
DialogicUtil.set_editor_setting('character_preview_fit', button_pressed)
|
||
|
update_preview()
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
## Open the reference manager
|
||
|
func _on_reference_manger_button_pressed():
|
||
|
editors_manager.reference_manager.open()
|
||
|
%PortraitChangeInfo.hide()
|
||
|
|