]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Modules/StyleEditor/style_editor.gd
Initial Godot project with Dialogic 2.0-Alpha-17
[wolf-seeking-sheep.git] / addons / dialogic / Modules / StyleEditor / style_editor.gd
1 @tool
2 extends DialogicEditor
3
4 ## Editor that handles the editing of styles and their layers.
5
6
7 var styles: Array[DialogicStyle] = []
8 var current_style : DialogicStyle = null
9 var default_style := ""
10
11 var premade_style_parts := {}
12
13 @onready var StyleList: ItemList = %StyleList
14
15 #region EDITOR MANAGEMENT
16 ################################################################################
17
18 func _get_title() -> String:
19         return "Styles"
20
21
22 func _get_icon() -> Texture:
23         return load(DialogicUtil.get_module_path('StyleEditor').path_join("styles_icon.svg"))
24
25
26 func _register() -> void:
27         editors_manager.register_simple_editor(self)
28         alternative_text = "Change the look of the dialog in your game"
29
30
31 func _open(_extra_info:Variant = null) -> void:
32         load_style_list()
33
34
35 func _close() -> void:
36         save_style_list()
37         save_style()
38
39
40 #endregion
41
42
43 func _ready() -> void:
44         collect_styles()
45
46         setup_ui()
47
48
49 #region STYLE MANAGEMENT
50 ################################################################################
51
52 func collect_styles() -> void:
53         for indexer in DialogicUtil.get_indexers():
54                 for layout in indexer._get_layout_parts():
55                         premade_style_parts[layout['path']] = layout
56
57         var style_list: Array = ProjectSettings.get_setting('dialogic/layout/style_list', [])
58         for style in style_list:
59                 if ResourceLoader.exists(style):
60                         if style != null:
61                                 styles.append(ResourceLoader.load(style, "DialogicStyle"))
62                         else:
63                                 print("[Dialogic] Failed to open style '", style, "'. Some dependency might be broken.")
64                 else:
65                         print("[Dialogic] Failed to open style '", style, "'. Might have been moved or deleted.")
66
67         default_style = ProjectSettings.get_setting('dialogic/layout/default_style', 'Default')
68
69
70 func save_style_list() -> void:
71         ProjectSettings.set_setting('dialogic/layout/style_list', styles.map(func(style:DialogicStyle): return style.resource_path))
72         ProjectSettings.set_setting('dialogic/layout/default_style', default_style)
73         ProjectSettings.save()
74
75
76 func save_style() -> void:
77         if current_style == null:
78                 return
79
80         ResourceSaver.save(current_style)
81
82
83 func add_style(file_path:String, style:DialogicStyle, inherits:DialogicStyle= null) -> void:
84         style.resource_path = file_path
85         style.inherits = inherits
86
87         if style.layer_list.is_empty() and style.inherits_anything():
88                 for id in style.get_layer_inherited_list():
89                         style.add_layer('', {}, id)
90
91         ResourceSaver.save(style, file_path)
92
93         styles.append(style)
94
95         if len(styles) == 1:
96                 default_style = style.resource_path
97
98         save_style_list()
99         load_style_list()
100         select_style(style)
101
102
103 func delete_style(style:DialogicStyle) -> void:
104         for other_style in styles:
105                 if other_style.inherits == style:
106                         other_style.realize_inheritance()
107                         push_warning('[Dialogic] Style "',other_style.name,'" had to be realized because it inherited "', style.name,'" which was deleted!')
108
109         if style.resource_path == default_style:
110                 default_style = ""
111         styles.erase(style)
112         save_style_list()
113
114
115 func delete_style_by_name(style_name:String) -> void:
116         for style in styles:
117                 if style.name == style_name:
118                         delete_style(style)
119                         return
120
121
122 func realize_style() -> void:
123         current_style.realize_inheritance()
124
125         select_style(current_style)
126
127
128 #endregion
129 #region USER INTERFACE
130 ################################################################################
131
132 func setup_ui() -> void:
133         %AddButton.icon = get_theme_icon("Add", "EditorIcons")
134         %DuplicateButton.icon = get_theme_icon("Duplicate", "EditorIcons")
135         %InheritanceButton.icon = get_theme_icon("GuiDropdown", "EditorIcons")
136         %RemoveButton.icon = get_theme_icon("Remove", "EditorIcons")
137
138         %EditNameButton.icon = get_theme_icon("Edit", "EditorIcons")
139         %TestStyleButton.icon = get_theme_icon("PlayCustom", "EditorIcons")
140         %MakeDefaultButton.icon = get_theme_icon("Favorites", "EditorIcons")
141
142         StyleList.item_selected.connect(_on_stylelist_selected)
143         %AddButton.get_popup().index_pressed.connect(_on_AddStyleMenu_selected)
144         %AddButton.about_to_popup.connect(_on_AddStyleMenu_about_to_popup)
145         %InheritanceButton.get_popup().index_pressed.connect(_on_inheritance_index_pressed)
146         StyleList.set_drag_forwarding(_on_stylelist_drag, _on_stylelist_can_drop, _on_style_list_drop)
147         %StyleView.hide()
148         %NoStyleView.show()
149
150 func load_style_list() -> void:
151         var latest: String = DialogicUtil.get_editor_setting('latest_layout_style', 'Default')
152
153         StyleList.clear()
154         var idx := 0
155         for style in styles:
156                 # TODO remove when going Beta
157                 style.update_from_pre_alpha16()
158                 StyleList.add_item(style.name, get_theme_icon("PopupMenu", "EditorIcons"))
159                 StyleList.set_item_tooltip(idx, style.resource_path)
160                 StyleList.set_item_metadata(idx, style)
161
162                 if style.resource_path == default_style:
163                         StyleList.set_item_icon_modulate(idx, get_theme_color("warning_color", "Editor"))
164                 if style.resource_path.begins_with("res://addons/dialogic"):
165                         StyleList.set_item_icon_modulate(idx, get_theme_color("property_color_z", "Editor"))
166                         StyleList.set_item_tooltip(idx, "This is a default style. Only edit it if you know what you are doing!")
167                         StyleList.set_item_custom_bg_color(idx, get_theme_color("property_color_z", "Editor").lerp(get_theme_color("dark_color_3", "Editor"), 0.8))
168                 if style.name == latest:
169                         StyleList.select(idx)
170                         load_style(style)
171                 idx += 1
172
173         if len(styles) == 0:
174                 %StyleView.hide()
175                 %NoStyleView.show()
176
177         elif !StyleList.is_anything_selected():
178                 StyleList.select(0)
179                 load_style(StyleList.get_item_metadata(0))
180
181
182 func _on_stylelist_selected(index:int) -> void:
183         load_style(StyleList.get_item_metadata(index))
184
185
186 func select_style(style:DialogicStyle) -> void:
187         DialogicUtil.set_editor_setting('latest_layout_style', style.name)
188         for idx in range(StyleList.item_count):
189                 if StyleList.get_item_metadata(idx) == style:
190                         StyleList.select(idx)
191                         return
192
193
194 func load_style(style:DialogicStyle) -> void:
195         if current_style != null:
196                 current_style.changed.disconnect(save_style)
197         save_style()
198         current_style = style
199         if current_style == null:
200                 return
201         current_style.changed.connect(save_style)
202
203         %LayoutStyleName.text = style.name
204         if style.resource_path == default_style:
205                 %MakeDefaultButton.tooltip_text = "Is Default"
206                 %MakeDefaultButton.disabled = true
207         else:
208                 %MakeDefaultButton.tooltip_text = "Make Default"
209                 %MakeDefaultButton.disabled = false
210
211         %StyleEditor.load_style(style)
212
213         %InheritanceButton.visible = style.inherits_anything()
214         if %InheritanceButton.visible:
215                 %InheritanceButton.text = "Inherits " + style.inherits.name
216
217
218         DialogicUtil.set_editor_setting('latest_layout_style', style.name)
219
220         %StyleView.show()
221         %NoStyleView.hide()
222
223
224 func _on_AddStyleMenu_about_to_popup() -> void:
225         %AddButton.get_popup().set_item_disabled(3, not StyleList.is_anything_selected())
226
227
228 func _on_AddStyleMenu_selected(index:int) -> void:
229         # add preset style
230         if index == 2:
231                 %StyleBrowserWindow.popup_centered_ratio(0.6)
232                 %StyleBrowser.current_type = 1
233                 %StyleBrowser.load_parts()
234                 var picked_info: Dictionary = await %StyleBrowserWindow.get_picked_info()
235                 if not picked_info.has('style_path'):
236                         return
237
238                 if not ResourceLoader.exists(picked_info.style_path):
239                         return
240
241                 var new_style: DialogicStyle = load(picked_info.style_path).clone()
242
243                 find_parent('EditorView').godot_file_dialog(
244                         add_style_undoable.bind(new_style),
245                         '*.tres',
246                         EditorFileDialog.FILE_MODE_SAVE_FILE,
247                         "Select folder for new style")
248
249         if index == 3:
250                 if StyleList.get_selected_items().is_empty():
251                         return
252                 find_parent('EditorView').godot_file_dialog(
253                         add_style_undoable.bind(DialogicStyle.new(), current_style),
254                         '*.tres',
255                         EditorFileDialog.FILE_MODE_SAVE_FILE,
256                         "Select folder for new style")
257
258         if index == 4:
259                 find_parent('EditorView').godot_file_dialog(
260                         add_style_undoable.bind(DialogicStyle.new()),
261                         '*.tres',
262                         EditorFileDialog.FILE_MODE_SAVE_FILE,
263                         "Select folder for new style")
264
265
266 func add_style_undoable(file_path:String, style:DialogicStyle, inherits:DialogicStyle = null) -> void:
267         style.name = _get_new_name(file_path.get_file().trim_suffix('.'+file_path.get_extension()))
268         var undo_redo: EditorUndoRedoManager = DialogicUtil.get_dialogic_plugin().get_undo_redo()
269         undo_redo.create_action('Add Style', UndoRedo.MERGE_ALL)
270         undo_redo.add_do_method(self, "add_style", file_path, style, inherits)
271         undo_redo.add_do_method(self, "load_style_list")
272         undo_redo.add_undo_method(self, "delete_style", style)
273         undo_redo.add_undo_method(self, "load_style_list")
274         undo_redo.commit_action()
275         DialogicUtil.set_editor_setting('latest_layout_style', style.name)
276
277
278 func _on_duplicate_button_pressed() -> void:
279         if !StyleList.is_anything_selected():
280                 return
281         find_parent('EditorView').godot_file_dialog(
282                 add_style_undoable.bind(current_style.clone(), null),
283                 '*.tres',
284                 EditorFileDialog.FILE_MODE_SAVE_FILE,
285                 "Select folder for new style")
286
287
288 func _on_remove_button_pressed() -> void:
289         if !StyleList.is_anything_selected():
290                 return
291
292         if current_style.name == default_style:
293                 push_warning("[Dialogic] You cannot delete the default style!")
294                 return
295
296         delete_style(current_style)
297         load_style_list()
298
299
300 func _on_edit_name_button_pressed() -> void:
301         %LayoutStyleName.grab_focus()
302         %LayoutStyleName.select_all()
303
304
305 func _on_layout_style_name_text_submitted(_new_text:String) -> void:
306         _on_layout_style_name_focus_exited()
307
308
309 func _on_layout_style_name_focus_exited() -> void:
310         var new_name: String = %LayoutStyleName.text.strip_edges()
311         if new_name == current_style.name:
312                 return
313
314         for style in styles:
315                 if style.name == new_name:
316                         %LayoutStyleName.text = current_style.name
317                         return
318
319         current_style.name = new_name
320         DialogicUtil.set_editor_setting('latest_layout_style', new_name)
321         load_style_list()
322
323
324 func _on_make_default_button_pressed() -> void:
325         default_style = current_style.resource_path
326         save_style_list()
327         load_style_list()
328
329
330
331 func _on_test_style_button_pressed() -> void:
332         var dialogic_plugin := DialogicUtil.get_dialogic_plugin()
333
334         # Save the current opened timeline
335         DialogicUtil.set_editor_setting('current_test_style', current_style.name)
336
337         DialogicUtil.get_dialogic_plugin().get_editor_interface().play_custom_scene("res://addons/dialogic/Editor/TimelineEditor/test_timeline_scene.tscn")
338         await get_tree().create_timer(3).timeout
339         DialogicUtil.set_editor_setting('current_test_style', '')
340
341
342 func _on_inheritance_index_pressed(index:int) -> void:
343         if index == 0:
344                 realize_style()
345
346
347
348 func _on_start_styling_button_pressed() -> void:
349         var new_style := DialogicUtil.get_fallback_style().clone()
350
351         find_parent('EditorView').godot_file_dialog(
352                 add_style_undoable.bind(new_style),
353                 '*.tres',
354                 EditorFileDialog.FILE_MODE_SAVE_FILE,
355                 "Select folder for new style")
356
357
358 #endregion
359
360 func _on_stylelist_drag(vector:Vector2) -> Variant:
361         return null
362
363
364 func _on_stylelist_can_drop(at_position: Vector2, data: Variant) -> bool:
365         if not data is Dictionary:
366                 return false
367         if not data.get('type', 's') == 'files':
368                 return false
369         for f in data.files:
370                 var style := load(f)
371                 if style is DialogicStyle:
372                         if not style in styles:
373                                 return true
374
375         return false
376
377 func _on_style_list_drop(at_position: Vector2, data: Variant) -> void:
378         for file in data.files:
379                 var style := load(file)
380                 if style is DialogicStyle:
381                         if not style in styles:
382                                 styles.append(style)
383         save_style_list()
384         load_style_list()
385
386
387 #region Helpers
388 func _get_new_name(base_name:String) -> String:
389         var new_name_idx := 1
390         var found_unique_name := false
391         var new_name := base_name
392         while not found_unique_name:
393                 found_unique_name = true
394                 for style in styles:
395                         if style.name == new_name:
396                                 new_name_idx += 1
397                                 new_name = base_name+" "+str(new_name_idx)
398                                 found_unique_name = false
399         return new_name
400
401 #endregion