]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Editor/CharacterEditor/character_editor.gd
Squashed commit of the following:
[wolf-seeking-sheep.git] / addons / dialogic / Editor / CharacterEditor / character_editor.gd
1 @tool
2 extends DialogicEditor
3
4 ## Editor for editing character resources.
5
6 signal character_loaded(resource_path:String)
7 signal portrait_selected()
8
9
10 # Current state
11 var loading := false
12 var current_previewed_scene: Variant = null
13 var current_scene_path: String = ""
14
15 # References
16 var selected_item: TreeItem
17 var def_portrait_path: String = DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn')
18
19
20 ######### EDITOR STUFF and LOADING/SAVING ######################################
21
22 #region Resource Logic
23 ## Method is called once editors manager is ready to accept registers.
24 func _register() -> void:
25         ## Makes the editor open this when a .dch file is selected.
26         ## Then _open_resource() is called.
27         editors_manager.register_resource_editor("dch", self)
28
29         ## Add an "add character" button
30         var add_character_button: Button = editors_manager.add_icon_button(
31                         load("res://addons/dialogic/Editor/Images/Toolbar/add-character.svg"),
32                         'Add Character',
33                         self)
34         add_character_button.pressed.connect(_on_create_character_button_pressed)
35         add_character_button.shortcut = Shortcut.new()
36         add_character_button.shortcut.events.append(InputEventKey.new())
37         add_character_button.shortcut.events[0].keycode = KEY_2
38         add_character_button.shortcut.events[0].ctrl_pressed = true
39
40         ## By default show the no character screen
41         $NoCharacterScreen.show()
42
43
44 func _get_title() -> String:
45         return "Character"
46
47
48 func _get_icon() -> Texture:
49         return load("res://addons/dialogic/Editor/Images/Resources/character.svg")
50
51
52 ## Called when a character is opened somehow
53 func _open_resource(resource:Resource) -> void:
54         if resource == null:
55                 $NoCharacterScreen.show()
56                 return
57
58         ## Update resource
59         current_resource = (resource as DialogicCharacter)
60
61         ## Make sure changes in the ui won't trigger saving
62         loading = true
63
64         ## Load other main tabs
65         for child in %MainSettingsSections.get_children():
66                 if child is DialogicCharacterEditorMainSection:
67                         child._load_character(current_resource)
68
69         ## Clear and then load Portrait section
70         %PortraitSearch.text = ""
71         load_portrait_tree()
72
73         loading = false
74         character_loaded.emit(resource.resource_path)
75
76         %CharacterName.text = DialogicResourceUtil.get_unique_identifier(resource.resource_path)
77
78         $NoCharacterScreen.hide()
79         %PortraitChangeInfo.hide()
80
81
82 ## Called when the character is opened.
83 func _open(extra_info:Variant="") -> void:
84         if !ProjectSettings.get_setting('dialogic/portraits/default_portrait', '').is_empty():
85                 def_portrait_path = ProjectSettings.get_setting('dialogic/portraits/default_portrait', '')
86         else:
87                 def_portrait_path = DialogicUtil.get_module_path('Character').path_join('default_portrait.tscn')
88
89         if current_resource == null:
90                 $NoCharacterScreen.show()
91                 return
92
93         update_preview(true)
94         %PortraitChangeInfo.hide()
95
96
97 func _clear() -> void:
98         current_resource = null
99         current_resource_state = ResourceStates.SAVED
100         $NoCharacterScreen.show()
101
102
103 func _save() -> void:
104         if ! visible or not current_resource:
105                 return
106
107         ## Portrait list
108         current_resource.portraits = get_updated_portrait_dict()
109
110         ## Main tabs
111         for child in %MainSettingsSections.get_children():
112                 if child is DialogicCharacterEditorMainSection:
113                         current_resource = child._save_changes(current_resource)
114
115         ResourceSaver.save(current_resource, current_resource.resource_path)
116         current_resource_state = ResourceStates.SAVED
117         DialogicResourceUtil.update_directory('dch')
118
119
120 ## Saves a new empty character to the given path
121 func new_character(path: String) -> void:
122         var resource := DialogicCharacter.new()
123         resource.resource_path = path
124         resource.display_name = path.get_file().trim_suffix("."+path.get_extension())
125         resource.color = Color(1,1,1,1)
126         resource.default_portrait = ""
127         resource.custom_info = {}
128         ResourceSaver.save(resource, path)
129         EditorInterface.get_resource_filesystem().update_file(path)
130         DialogicResourceUtil.update_directory('dch')
131         editors_manager.edit_resource(resource)
132
133 #endregion
134
135
136 ######### INTERFACE ############################################################
137
138 #region Interface
139 func _ready() -> void:
140         if get_parent() is SubViewport:
141                 return
142
143         DialogicUtil.get_dialogic_plugin().resource_saved.connect(_on_some_resource_saved)
144         # NOTE: This check is required because up to 4.2 this signal is not exposed.
145         if DialogicUtil.get_dialogic_plugin().has_signal("scene_saved"):
146                 DialogicUtil.get_dialogic_plugin().scene_saved.connect(_on_some_resource_saved)
147
148         $NoCharacterScreen.color = get_theme_color("dark_color_2", "Editor")
149         $NoCharacterScreen.show()
150         setup_portrait_list_tab()
151
152         _on_fit_preview_toggle_toggled(DialogicUtil.get_editor_setting('character_preview_fit', true))
153         %PreviewLabel.add_theme_color_override("font_color", get_theme_color("readonly_color", "Editor"))
154
155         %PortraitChangeWarning.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
156
157         %RealPreviewPivot.texture = get_theme_icon("EditorPivot", "EditorIcons")
158
159         %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
160
161         set_portrait_settings_position(DialogicUtil.get_editor_setting('portrait_settings_position', true))
162
163         await find_parent('EditorView').ready
164
165         ## Add general tabs
166         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_general.tscn").instantiate(), %MainSettingsSections)
167         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_section_portraits.tscn").instantiate(), %MainSettingsSections)
168         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/character_prefix_suffix.tscn").instantiate(), %MainSettingsSections)
169
170
171         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main_exports.tscn").instantiate(), %PortraitSettingsSection)
172         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_exports.tscn").instantiate(), %PortraitSettingsSection)
173         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_main.tscn").instantiate(), %PortraitSettingsSection)
174         add_settings_section(load("res://addons/dialogic/Editor/CharacterEditor/char_edit_p_section_layout.tscn").instantiate(), %PortraitSettingsSection)
175
176         ## Load custom sections from modules
177         for indexer in DialogicUtil.get_indexers():
178                 for path in indexer._get_character_editor_sections():
179                         var scene: Control = load(path).instantiate()
180                         if scene is DialogicCharacterEditorMainSection:
181                                 add_settings_section(scene, %MainSettingsSections)
182                         elif scene is DialogicCharacterEditorPortraitSection:
183                                 add_settings_section(scene, %PortraitSettingsSection)
184
185
186 ## Add a section (a control) either to the given settings section (Main or Portraits)
187 ## - sets up the title of the section
188 ## - connects to various signals
189 func add_settings_section(edit:Control, parent:Node) ->  void:
190         edit.changed.connect(something_changed)
191         edit.character_editor = self
192
193         if edit.has_signal('update_preview'):
194                 edit.update_preview.connect(update_preview)
195
196         var button: Button
197
198         if edit._show_title():
199                 var hbox := HBoxContainer.new()
200                 hbox.name = edit._get_title()+"BOX"
201                 button = Button.new()
202                 button.flat = true
203                 button.theme_type_variation = "DialogicSection"
204                 button.alignment = HORIZONTAL_ALIGNMENT_LEFT
205                 button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
206                 button.text = edit._get_title()
207                 button.icon_alignment = HORIZONTAL_ALIGNMENT_RIGHT
208                 button.pressed.connect(_on_section_button_pressed.bind(button))
209                 button.focus_mode = Control.FOCUS_NONE
210                 button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons")
211                 button.add_theme_color_override('icon_normal_color', get_theme_color("font_color", "DialogicSection"))
212
213                 hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
214                 hbox.add_child(button)
215
216                 if !edit.hint_text.is_empty():
217                         var hint: Node = load("res://addons/dialogic/Editor/Common/hint_tooltip_icon.tscn").instantiate()
218                         hint.hint_text = edit.hint_text
219                         hbox.add_child(hint)
220
221                 parent.add_child(hbox)
222         parent.add_child(edit)
223         parent.add_child(HSeparator.new())
224         if button and !edit._start_opened():
225                 _on_section_button_pressed(button)
226
227
228 func get_settings_section_by_name(name:String, main:=true) -> Node:
229         var parent := %MainSettingsSections
230         if not main:
231                 parent = %PortraitSettingsSection
232
233         if parent.has_node(name):
234                 return parent.get_node(name)
235         elif parent.has_node(name+"BOX/"+name):
236                 return parent.get_node(name+"BOX/"+name)
237         else:
238                 return null
239
240
241 func _on_section_button_pressed(button:Button) -> void:
242         var section_header := button.get_parent()
243         var section := section_header.get_parent().get_child(section_header.get_index()+1)
244         if section.visible:
245                 button.icon = get_theme_icon("CodeFoldedRightArrow", "EditorIcons")
246                 section.visible = false
247         else:
248                 button.icon = get_theme_icon("CodeFoldDownArrow", "EditorIcons")
249                 section.visible = true
250
251         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:
252                 section_header.get_parent().get_child(section_header.get_index()+2).visible = section_header.get_parent().get_child(section_header.get_index()+1).visible
253
254
255 func something_changed(fake_argument = "", fake_arg2 = null) -> void:
256         if not loading:
257                 current_resource_state = ResourceStates.UNSAVED
258
259
260 func _on_main_settings_collapse_toggled(button_pressed:bool) -> void:
261         %MainSettingsTitle.visible = !button_pressed
262         %MainSettingsScroll.visible = !button_pressed
263         if button_pressed:
264                 %MainSettings.hide()
265                 %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityHidden", "EditorIcons")
266         else:
267                 %MainSettings.show()
268                 %MainSettingsCollapse.icon = get_theme_icon("GuiVisibilityVisible", "EditorIcons")
269
270
271 func _on_switch_portrait_settings_position_pressed() -> void:
272         set_portrait_settings_position(!%RightSection.vertical)
273
274
275 func set_portrait_settings_position(is_below:bool) -> void:
276         %RightSection.vertical = is_below
277         DialogicUtil.set_editor_setting('portrait_settings_position', is_below)
278         if is_below:
279                 %SwitchPortraitSettingsPosition.icon = get_theme_icon("ControlAlignRightWide", "EditorIcons")
280         else:
281                 %SwitchPortraitSettingsPosition.icon = get_theme_icon("ControlAlignBottomWide", "EditorIcons")
282
283 #endregion
284
285
286 ########## PORTRAIT SECTION ####################################################
287
288 #region Portrait Section
289 func setup_portrait_list_tab() -> void:
290         %PortraitTree.editor = self
291
292         ## Portrait section styling/connections
293         %AddPortraitButton.icon = get_theme_icon("Add", "EditorIcons")
294         %AddPortraitButton.pressed.connect(add_portrait)
295         %AddPortraitGroupButton.icon = load("res://addons/dialogic/Editor/Images/Pieces/add-folder.svg")
296         %AddPortraitGroupButton.pressed.connect(add_portrait_group)
297         %ImportPortraitsButton.icon = get_theme_icon("Load", "EditorIcons")
298         %ImportPortraitsButton.pressed.connect(open_portrait_folder_select)
299         %PortraitSearch.right_icon = get_theme_icon("Search", "EditorIcons")
300         %PortraitSearch.text_changed.connect(filter_portrait_list)
301
302         %PortraitTree.item_selected.connect(load_selected_portrait)
303         %PortraitTree.item_edited.connect(_on_item_edited)
304         %PortraitTree.item_activated.connect(_on_item_activated)
305
306
307 func open_portrait_folder_select() -> void:
308         find_parent("EditorView").godot_file_dialog(
309                 import_portraits_from_folder, "*.svg, *.png",
310                 EditorFileDialog.FILE_MODE_OPEN_DIR)
311
312
313 func import_portraits_from_folder(path:String) -> void:
314         var parent: TreeItem = %PortraitTree.get_root()
315
316         if %PortraitTree.get_selected() and %PortraitTree.get_selected() != parent and %PortraitTree.get_selected().get_metadata(0).has('group'):
317                 parent = %PortraitTree.get_selected()
318
319         var dir := DirAccess.open(path)
320         dir.list_dir_begin()
321         var file_name: String = dir.get_next()
322         var files := []
323         while file_name != "":
324                 if not dir.current_is_dir():
325                         var file_lower := file_name.to_lower()
326                         if '.svg' in file_lower or '.png' in file_lower:
327                                 if not '.import' in file_lower:
328                                         files.append(file_name)
329                 file_name = dir.get_next()
330
331         var prefix: String = files[0]
332         for file in files:
333                 while true:
334                         if file.begins_with(prefix):
335                                 break
336                         if prefix.is_empty():
337                                 break
338                         prefix = prefix.substr(0, len(prefix)-1)
339
340         for file in files:
341                 %PortraitTree.add_portrait_item(file.trim_prefix(prefix).trim_suffix('.'+file.get_extension()),
342                         {'scene':"",'export_overrides':{'image':var_to_str(path.path_join(file))}, 'scale':1, 'offset':Vector2(), 'mirror':false}, parent)
343
344         ## Handle selection
345         if parent.get_child_count():
346                 parent.get_first_child().select(0)
347         else:
348                 # Call anyways to clear preview and hide portrait settings section
349                 load_selected_portrait()
350
351         something_changed()
352
353
354 func add_portrait(portrait_name:String='New portrait', portrait_data:Dictionary={'scene':"", 'export_overrides':{'image':''}, 'scale':1, 'offset':Vector2(), 'mirror':false}) -> void:
355         var parent: TreeItem = %PortraitTree.get_root()
356         if %PortraitTree.get_selected():
357                 if %PortraitTree.get_selected().get_metadata(0) and %PortraitTree.get_selected().get_metadata(0).has('group'):
358                         parent = %PortraitTree.get_selected()
359                 else:
360                         parent = %PortraitTree.get_selected().get_parent()
361         var item: TreeItem = %PortraitTree.add_portrait_item(portrait_name, portrait_data, parent)
362         item.set_meta('new', true)
363         item.set_editable(0, true)
364         item.select(0)
365         %PortraitTree.call_deferred('edit_selected')
366         something_changed()
367
368
369 func add_portrait_group() -> void:
370         var parent_item: TreeItem = %PortraitTree.get_root()
371         if %PortraitTree.get_selected() and %PortraitTree.get_selected().get_metadata(0).has('group'):
372                 parent_item = %PortraitTree.get_selected()
373         var item: TreeItem = %PortraitTree.add_portrait_group("Group", parent_item)
374         item.set_meta('new', true)
375         item.set_editable(0, true)
376         item.select(0)
377         %PortraitTree.call_deferred('edit_selected')
378
379
380 func load_portrait_tree() -> void:
381         %PortraitTree.clear_tree()
382         var root: TreeItem = %PortraitTree.create_item()
383
384         for portrait in current_resource.portraits.keys():
385                 var portrait_label: String = portrait
386                 var parent: TreeItem = %PortraitTree.get_root()
387                 if '/' in portrait:
388                         parent = %PortraitTree.create_necessary_group_items(portrait)
389                         portrait_label = portrait.split('/')[-1]
390
391                 %PortraitTree.add_portrait_item(portrait_label, current_resource.portraits[portrait], parent)
392
393         update_default_portrait_star(current_resource.default_portrait)
394
395         if root.get_child_count():
396                 root.get_first_child().select(0)
397                 while %PortraitTree.get_selected().get_child_count():
398                         %PortraitTree.get_selected().get_child(0).select(0)
399         else:
400                 # Call anyways to clear preview and hide portrait settings section
401                 load_selected_portrait()
402
403
404 func filter_portrait_list(filter_term := "") -> void:
405         filter_branch(%PortraitTree.get_root(), filter_term)
406
407
408 func filter_branch(parent: TreeItem, filter_term: String) -> bool:
409         var anything_visible := false
410         for item in parent.get_children():
411                 if item.get_metadata(0).has('group'):
412                         item.visible = filter_branch(item, filter_term)
413                         anything_visible = item.visible
414                 elif filter_term.is_empty() or filter_term.to_lower() in item.get_text(0).to_lower():
415                         item.visible = true
416                         anything_visible = true
417                 else:
418                         item.visible = false
419         return anything_visible
420
421
422 ## This is used to save the portrait data
423 func get_updated_portrait_dict() -> Dictionary:
424         return list_portraits(%PortraitTree.get_root().get_children())
425
426
427 func list_portraits(tree_items: Array[TreeItem], dict := {}, path_prefix := "") -> Dictionary:
428         for item in tree_items:
429                 if item.get_metadata(0).has('group'):
430                         dict = list_portraits(item.get_children(), dict, path_prefix+item.get_text(0)+"/")
431                 else:
432                         dict[path_prefix +item.get_text(0)] = item.get_metadata(0)
433         return dict
434
435
436 func load_selected_portrait() -> void:
437         if selected_item and is_instance_valid(selected_item):
438                 selected_item.set_editable(0, false)
439
440         selected_item = %PortraitTree.get_selected()
441
442         if selected_item and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'):
443                 %PortraitSettingsSection.show()
444                 var current_portrait_data: Dictionary = selected_item.get_metadata(0)
445                 portrait_selected.emit(%PortraitTree.get_full_item_name(selected_item), current_portrait_data)
446
447                 update_preview()
448
449                 for child in %PortraitSettingsSection.get_children():
450                         if child is DialogicCharacterEditorPortraitSection:
451                                 child.selected_item = selected_item
452                                 child._load_portrait_data(current_portrait_data)
453
454         else:
455                 %PortraitSettingsSection.hide()
456                 update_preview()
457
458
459 func delete_portrait_item(item: TreeItem) -> void:
460         if item.get_next_visible(true) and item.get_next_visible(true) != item:
461                 item.get_next_visible(true).select(0)
462         else:
463                 selected_item = null
464                 load_selected_portrait()
465         item.free()
466         something_changed()
467
468
469 func duplicate_item(item: TreeItem) -> void:
470         var new_item: TreeItem = %PortraitTree.add_portrait_item(item.get_text(0)+'_duplicated', item.get_metadata(0).duplicate(true), item.get_parent())
471         new_item.set_meta('new', true)
472         new_item.select(0)
473
474
475 func _input(event: InputEvent) -> void:
476         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())):
477                 return
478         if event is InputEventKey and event.pressed:
479                 if event.keycode == KEY_F2 and %PortraitTree.get_selected():
480                         %PortraitTree.get_selected().set_editable(0, true)
481                         %PortraitTree.edit_selected()
482                         get_viewport().set_input_as_handled()
483                 elif event.keycode == KEY_DELETE and get_viewport().gui_get_focus_owner() is Tree and %PortraitTree.get_selected():
484                         delete_portrait_item(%PortraitTree.get_selected())
485                         get_viewport().set_input_as_handled()
486
487
488 func _on_portrait_right_click_menu_index_pressed(id: int) -> void:
489         # RENAME BUTTON
490         if id == 0:
491                 _on_item_activated()
492         # DELETE BUTTON
493         if id == 2:
494                 delete_portrait_item(%PortraitTree.get_selected())
495         # DUPLICATE ITEM
496         elif id == 1:
497                 duplicate_item(%PortraitTree.get_selected())
498         elif id == 4:
499                 get_settings_section_by_name("Portraits").set_default_portrait(%PortraitTree.get_full_item_name(%PortraitTree.get_selected()))
500
501
502 ## This removes/and adds the DEFAULT star on the portrait list
503 func update_default_portrait_star(default_portrait_name: String) -> void:
504         var item_list: Array = %PortraitTree.get_root().get_children()
505         if item_list.is_empty() == false:
506                 while true:
507                         var item: TreeItem = item_list.pop_back()
508                         if item.get_button_by_id(0, 2) != -1:
509                                 item.erase_button(0, item.get_button_by_id(0, 2))
510                         if %PortraitTree.get_full_item_name(item) == default_portrait_name:
511                                 item.add_button(0, get_theme_icon("Favorites", "EditorIcons"), 2, true, "Default")
512                         item_list.append_array(item.get_children())
513                         if item_list.is_empty():
514                                 break
515
516
517 func _on_item_edited() -> void:
518         selected_item = %PortraitTree.get_selected()
519         something_changed()
520         if selected_item:
521                 if %PreviewLabel.text.trim_prefix('Preview of "').trim_suffix('"') == current_resource.default_portrait:
522                         current_resource.default_portrait = %PortraitTree.get_full_item_name(selected_item)
523                 selected_item.set_editable(0, false)
524
525                 if !selected_item.has_meta('new') and %PortraitTree.get_full_item_name(selected_item) != selected_item.get_meta('previous_name'):
526                         report_name_change(selected_item)
527                         %PortraitChangeInfo.show()
528         update_preview()
529
530
531 func _on_item_activated() -> void:
532         if %PortraitTree.get_selected() == null:
533                 return
534         %PortraitTree.get_selected().set_editable(0, true)
535         %PortraitTree.edit_selected()
536
537
538 func report_name_change(item: TreeItem) -> void:
539         if item.get_metadata(0).has('group'):
540                 for s_item in item.get_children():
541                         if s_item.get_metadata(0).has('group') or !s_item.has_meta('new'):
542                                 report_name_change(s_item)
543         else:
544                 if item.get_meta('previous_name') == %PortraitTree.get_full_item_name(item):
545                         return
546                 editors_manager.reference_manager.add_portrait_ref_change(
547                         item.get_meta('previous_name'),
548                         %PortraitTree.get_full_item_name(item),
549                         [DialogicResourceUtil.get_unique_identifier(current_resource.resource_path)])
550         item.set_meta('previous_name', %PortraitTree.get_full_item_name(item))
551         %PortraitChangeInfo.show()
552
553 #endregion
554
555 ########### PREVIEW ############################################################
556
557 #region Preview
558 func update_preview(force := false, ignore_settings_reload := false) -> void:
559         %ScenePreviewWarning.hide()
560
561         if selected_item and is_instance_valid(selected_item) and selected_item.get_metadata(0) != null and !selected_item.get_metadata(0).has('group'):
562                 %PreviewLabel.text = 'Preview of "'+%PortraitTree.get_full_item_name(selected_item)+'"'
563
564                 var current_portrait_data: Dictionary = selected_item.get_metadata(0)
565
566                 if not force and current_previewed_scene != null \
567                         and scene_file_path == current_portrait_data.get('scene') \
568                         and current_previewed_scene.has_method('_should_do_portrait_update') \
569                         and is_instance_valid(current_previewed_scene.get_script()) \
570                         and current_previewed_scene._should_do_portrait_update(current_resource, selected_item.get_text(0)):
571                         # We keep the same scene.
572                         pass
573                 else:
574
575                         for node in %RealPreviewPivot.get_children():
576                                 node.queue_free()
577
578                         current_previewed_scene = null
579                         current_scene_path = ""
580
581                         var scene_path := def_portrait_path
582                         if not current_portrait_data.get('scene', '').is_empty():
583                                 scene_path = current_portrait_data.get('scene')
584
585                         if ResourceLoader.exists(scene_path):
586                                 current_previewed_scene = load(scene_path).instantiate()
587                                 current_scene_path = scene_path
588
589                         if not current_previewed_scene == null:
590                                 %RealPreviewPivot.add_child(current_previewed_scene)
591
592                 if not current_previewed_scene == null:
593                         var scene: Node = current_previewed_scene
594
595                         scene.show_behind_parent = true
596                         DialogicUtil.apply_scene_export_overrides(scene, current_portrait_data.get('export_overrides', {}))
597
598                         var mirror: bool = current_portrait_data.get('mirror', false) != current_resource.mirror
599                         var scale: float = current_portrait_data.get('scale', 1) * current_resource.scale
600
601                         if current_portrait_data.get('ignore_char_scale', false):
602                                 scale = current_portrait_data.get('scale', 1)
603
604                         var offset: Vector2 = current_portrait_data.get('offset', Vector2()) + current_resource.offset
605
606                         if is_instance_valid(scene.get_script()) and scene.script.is_tool():
607
608                                 if scene.has_method('_update_portrait'):
609                                         ## Create a fake duplicate resource that has all the portrait changes applied already
610                                         var preview_character := current_resource.duplicate()
611                                         preview_character.portraits = get_updated_portrait_dict()
612                                         scene._update_portrait(preview_character, %PortraitTree.get_full_item_name(selected_item))
613
614                                 if scene.has_method('_set_mirror'):
615                                         scene._set_mirror(mirror)
616
617                         if !%FitPreview_Toggle.button_pressed:
618                                 scene.position = Vector2() + offset
619                                 scene.scale = Vector2(1,1)*scale
620                         else:
621
622                                 if not scene.get_script() == null and scene.script.is_tool() and scene.has_method('_get_covered_rect'):
623                                         var rect: Rect2 = scene._get_covered_rect()
624                                         var available_rect: Rect2 = %FullPreviewAvailableRect.get_rect()
625                                         scene.scale = Vector2(1,1) * min(available_rect.size.x/rect.size.x, available_rect.size.y/rect.size.y)
626                                         %RealPreviewPivot.position = (rect.position)*-1*scene.scale
627                                         %RealPreviewPivot.position.x = %FullPreviewAvailableRect.size.x/2
628                                         scene.position = Vector2()
629
630                                 else:
631                                         %ScenePreviewWarning.show()
632                 else:
633                         %PreviewLabel.text = 'Nothing to preview'
634
635                 if not ignore_settings_reload:
636                         for child in %PortraitSettingsSection.get_children():
637                                 if child is DialogicCharacterEditorPortraitSection:
638                                         child._recheck(current_portrait_data)
639
640         else:
641                 %PreviewLabel.text = 'No portrait to preview.'
642
643                 for node in %RealPreviewPivot.get_children():
644                         node.queue_free()
645
646                 current_previewed_scene = null
647                 current_scene_path = ""
648
649
650 func _on_some_resource_saved(file:Variant) -> void:
651         if current_previewed_scene == null:
652                 return
653
654         if file is Resource and file == current_previewed_scene.script:
655                 update_preview(true)
656
657         if typeof(file) == TYPE_STRING and file == current_previewed_scene.get_meta("path", ""):
658                 update_preview(true)
659
660
661 func _on_full_preview_available_rect_resized() -> void:
662         if %FitPreview_Toggle.button_pressed:
663                 update_preview(false, true)
664
665
666 func _on_create_character_button_pressed() -> void:
667         editors_manager.show_add_resource_dialog(
668                         new_character,
669                         '*.dch; DialogicCharacter',
670                         'Create new character',
671                         'character',
672                         )
673
674
675 func _on_fit_preview_toggle_toggled(button_pressed):
676         %FitPreview_Toggle.set_pressed_no_signal(button_pressed)
677         if button_pressed:
678                 %FitPreview_Toggle.icon = get_theme_icon("ScrollContainer", "EditorIcons")
679                 %FitPreview_Toggle.tooltip_text = "Real scale"
680         else:
681                 %FitPreview_Toggle.tooltip_text = "Fit into preview"
682                 %FitPreview_Toggle.icon = get_theme_icon("CenterContainer", "EditorIcons")
683         DialogicUtil.set_editor_setting('character_preview_fit', button_pressed)
684         update_preview(false, true)
685
686 #endregion
687
688 ## Open the reference manager
689 func _on_reference_manger_button_pressed() -> void:
690         editors_manager.reference_manager.open()
691         %PortraitChangeInfo.hide()