]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Editor/editors_manager.gd
Adding import files
[wolf-seeking-sheep.git] / addons / dialogic / Editor / editors_manager.gd
1 @tool
2 extends Control
3
4 ## Node that manages editors, the toolbar and  the sidebar.
5
6 signal resource_opened(resource)
7 signal editor_changed(previous, current)
8
9 ### References
10 @onready var hsplit := $HSplit
11 @onready var sidebar := $HSplit/Sidebar
12 @onready var editors_holder := $HSplit/VBox/Editors
13 @onready var toolbar := $HSplit/VBox/Toolbar
14 @onready var tabbar := $HSplit/VBox/Toolbar/EditorTabBar
15
16 var reference_manager: Node:
17         get:
18                 return get_node("../ReferenceManager")
19
20 ## Information on supported resource extensions and registered editors
21 var current_editor: DialogicEditor = null
22 var previous_editor: DialogicEditor = null
23 var editors := {}
24 var supported_file_extensions := []
25 var used_resources_cache: Array = []
26
27
28 ################################################################################
29 ##                                              REGISTERING EDITORS
30 ################################################################################
31
32 ## Asks all childs of the editor holder to register
33 func _ready() -> void:
34         if owner.get_parent() is SubViewport:
35                 return
36
37         tabbar.clear_tabs()
38
39         # Load base editors
40         _add_editor("res://addons/dialogic/Editor/HomePage/home_page.tscn")
41         _add_editor("res://addons/dialogic/Editor/TimelineEditor/timeline_editor.tscn")
42         _add_editor("res://addons/dialogic/Editor/CharacterEditor/character_editor.tscn")
43
44         # Load custom editors
45         for indexer in DialogicUtil.get_indexers():
46                 for editor_path in indexer._get_editors():
47                         _add_editor(editor_path)
48         _add_editor("res://addons/dialogic/Editor/Settings/settings_editor.tscn")
49
50         tabbar.tab_clicked.connect(_on_editors_tab_changed)
51
52         # Needs to be done here to make sure this node is ready when doing the register calls
53         for editor in editors_holder.get_children():
54                 editor.editors_manager = self
55                 editor._register()
56
57         DialogicResourceUtil.update()
58
59         await get_parent().ready
60         await get_tree().process_frame
61
62         load_saved_state()
63         used_resources_cache = DialogicUtil.get_editor_setting('last_resources', [])
64         sidebar.update_resource_list(used_resources_cache)
65
66         find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().files_moved.connect(_on_file_moved)
67         find_parent('EditorView').plugin_reference.get_editor_interface().get_file_system_dock().file_removed.connect(_on_file_removed)
68
69         hsplit.set("theme_override_constants/separation", get_theme_constant("base_margin", "Editor") * DialogicUtil.get_editor_scale())
70
71
72 func _add_editor(path:String) -> void:
73         var editor: DialogicEditor = load(path).instantiate()
74         editors_holder.add_child(editor)
75         editor.hide()
76         tabbar.add_tab(editor._get_title(), editor._get_icon())
77
78
79 ## Call to register an editor/tab that edits a resource with a custom ending.
80 func register_resource_editor(resource_extension:String, editor:DialogicEditor) -> void:
81         editors[editor.name] = {'node':editor, 'buttons':[], 'extension': resource_extension}
82         supported_file_extensions.append(resource_extension)
83         editor.resource_saved.connect(_on_resource_saved.bind(editor))
84         editor.resource_unsaved.connect(_on_resource_unsaved.bind(editor))
85
86
87 ## Call to register an editor/tab that doesn't edit a resource
88 func register_simple_editor(editor:DialogicEditor) -> void:
89         editors[editor.name] = {'node': editor,  'buttons':[]}
90
91
92 ## Call to add an icon button. These buttons are always visible.
93 func add_icon_button(icon:Texture, tooltip:String, editor:DialogicEditor=null) -> Node:
94         var button: Button = toolbar.add_icon_button(icon, tooltip)
95         if editor != null:
96                 editors[editor.name]['buttons'].append(button)
97         return button
98
99
100 ## Call to add a custom action button. Only visible if editor is visible.
101 func add_custom_button(label:String, icon:Texture, editor:DialogicEditor) -> Node:
102         var button: Button = toolbar.add_custom_button(label, icon)
103         editors[editor.name]['buttons'].append(button)
104         return button
105
106
107 func can_edit_resource(resource:Resource) -> bool:
108         return resource.resource_path.get_extension() in supported_file_extensions
109
110
111 ################################################################################
112 ##                                              OPENING/CLOSING
113 ################################################################################
114
115
116 func _on_editors_tab_changed(tab:int) -> void:
117         open_editor(editors_holder.get_child(tab))
118
119
120 func edit_resource(resource:Resource, save_previous:bool = true, silent:= false) -> void:
121         if not resource:
122                 # The resource doesn't exists, show an error
123                 print("[Dialogic] The resource you are trying to edit doesn't exist any more.")
124                 return
125
126         if current_editor and save_previous:
127                 current_editor._save()
128
129         if !resource.resource_path in used_resources_cache:
130                 used_resources_cache.append(resource.resource_path)
131                 sidebar.update_resource_list(used_resources_cache)
132
133         ## Open the correct editor
134         var extension: String = resource.resource_path.get_extension()
135         for editor in editors.values():
136                 if editor.get('extension', '') == extension:
137                         editor['node']._open_resource(resource)
138                         if !silent:
139                                 open_editor(editor['node'], false)
140         if !silent:
141                 resource_opened.emit(resource)
142
143
144
145 ## Only works if there was a different editor opened previously
146 func toggle_editor(editor) -> void:
147         if editor.visible:
148                 open_editor(previous_editor, true)
149         else:
150                 open_editor(editor, true)
151
152
153 ## Shows the given editor
154 func open_editor(editor:DialogicEditor, save_previous: bool = true, extra_info:Variant = null) -> void:
155         if current_editor and save_previous:
156                 current_editor._save()
157
158         if current_editor:
159                 current_editor._close()
160                 current_editor.hide()
161
162         if current_editor != previous_editor:
163                 previous_editor = current_editor
164
165         editor._open(extra_info)
166         editor.opened.emit()
167         current_editor = editor
168         editor.show()
169         tabbar.current_tab = editor.get_index()
170
171         if editor.current_resource:
172                 var text: String = editor.current_resource.resource_path.get_file()
173                 if editor.current_resource_state == DialogicEditor.ResourceStates.UNSAVED:
174                         text += "(*)"
175
176         ## This makes custom button editor-specific
177         ## I think it's better without.
178
179         save_current_state()
180         editor_changed.emit(previous_editor, current_editor)
181
182
183 ## Rarely used to completely clear an editor.
184 func clear_editor(editor:DialogicEditor, save:bool = false) -> void:
185         if save:
186                 editor._save()
187
188         editor._clear()
189
190 ## Shows a file selector. Calls [accept_callable] once accepted
191 func show_add_resource_dialog(accept_callable:Callable, filter:String = "*", title = "New resource", default_name = "new_character", mode = EditorFileDialog.FILE_MODE_SAVE_FILE) -> void:
192         find_parent('EditorView').godot_file_dialog(
193                 _on_add_resource_dialog_accepted.bind(accept_callable),
194                 filter,
195                 mode,
196                 title,
197                 default_name,
198                 true,
199                 "Do not use \"'()!;:/\\*# in character or timeline names!"
200         )
201
202
203 func _on_add_resource_dialog_accepted(path:String, callable:Callable) -> void:
204         var file_name: String = path.get_file().trim_suffix('.'+path.get_extension())
205         for i in ['#','&','+',';','(',')','!','*','*','"',"'",'%', '$', ':','.',',']:
206                 file_name = file_name.replace(i, '')
207         callable.call(path.trim_suffix(path.get_file()).path_join(file_name)+'.'+path.get_extension())
208
209
210 ## Called by the plugin.gd script on CTRL+S or Debug Game start
211 func save_current_resource() -> void:
212         if current_editor:
213                 current_editor._save()
214
215
216 ## Change the resource state
217 func _on_resource_saved(editor:DialogicEditor):
218         sidebar.set_unsaved_indicator(true)
219
220
221 ## Change the resource state
222 func _on_resource_unsaved(editor:DialogicEditor):
223         sidebar.set_unsaved_indicator(false)
224
225
226 ## Tries opening the last resource
227 func load_saved_state() -> void:
228         var current_resources: Dictionary = DialogicUtil.get_editor_setting('current_resources', {})
229         for editor in current_resources.keys():
230                 editors[editor]['node']._open_resource(load(current_resources[editor]))
231
232         var current_editor: String = DialogicUtil.get_editor_setting('current_editor', 'HomePage')
233         open_editor(editors[current_editor]['node'])
234
235
236 func save_current_state() -> void:
237         DialogicUtil.set_editor_setting('current_editor', current_editor.name)
238         var current_resources: Dictionary = {}
239         for editor in editors.values():
240                 if editor['node'].current_resource != null:
241                         current_resources[editor['node'].name] = editor['node'].current_resource.resource_path
242         DialogicUtil.set_editor_setting('current_resources', current_resources)
243
244
245 func _on_file_moved(old_name:String, new_name:String) -> void:
246         if !old_name.get_extension() in supported_file_extensions:
247                 return
248
249         used_resources_cache = DialogicUtil.get_editor_setting('last_resources', [])
250         if old_name in used_resources_cache:
251                 used_resources_cache.insert(used_resources_cache.find(old_name), new_name)
252                 used_resources_cache.erase(old_name)
253
254         sidebar.update_resource_list(used_resources_cache)
255
256         for editor in editors:
257                 if editors[editor].node.current_resource != null and editors[editor].node.current_resource.resource_path == old_name:
258                         editors[editor].node.current_resource.take_over_path(new_name)
259                         edit_resource(load(new_name), true, true)
260
261         save_current_state()
262
263
264 func _on_file_removed(file_name:String) -> void:
265         var current_resources: Dictionary = DialogicUtil.get_editor_setting('current_resources', {})
266         for editor_name in current_resources:
267                 if current_resources[editor_name] == file_name:
268                         clear_editor(editors[editor_name].node, false)
269                         sidebar.update_resource_list()
270                         save_current_state()
271
272
273
274 ################################################################################
275 ##                                              HELPERS
276 ################################################################################
277
278
279 func get_current_editor() -> DialogicEditor:
280         return current_editor
281
282
283 func _exit_tree() -> void:
284         DialogicUtil.set_editor_setting('last_resources', used_resources_cache)