]> Untitled Git - wolf-seeking-sheep.git/blob - addons/dialogic/Modules/Glossary/glossary_editor.gd
Initial Godot project with Dialogic 2.0-Alpha-17
[wolf-seeking-sheep.git] / addons / dialogic / Modules / Glossary / glossary_editor.gd
1 @tool
2 extends DialogicEditor
3
4 var current_glossary: DialogicGlossary = null
5 var current_entry_name := ""
6 var current_entry := {}
7
8 ################################################################################
9 ##                                      BASICS
10 ################################################################################
11
12 func _get_title() -> String:
13         return "Glossary"
14
15
16 func _get_icon() -> Texture:
17         var base_directory: String = self.get_script().get_path().get_base_dir()
18         var icon_path := base_directory + "/icon.svg"
19         return load(icon_path)
20
21
22 func _register() -> void:
23         editors_manager.register_simple_editor(self)
24         alternative_text = "Create and edit glossaries."
25
26
27 func _ready() -> void:
28         var add_glossary_icon_path: String = self.get_script().get_path().get_base_dir() + "/add-glossary.svg"
29         var add_glossary_icon := load(add_glossary_icon_path)
30         %AddGlossaryFile.icon = add_glossary_icon
31
32         %LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons')
33         %DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons')
34         %DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons')
35
36         %DeleteGlossaryFile.pressed.connect(_on_delete_glossary_file_pressed)
37
38         %AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons')
39         %EntrySearch.right_icon = get_theme_icon('Search', 'EditorIcons')
40
41         %GlossaryList.item_selected.connect(_on_GlossaryList_item_selected)
42         %EntryList.item_selected.connect(_on_EntryList_item_selected)
43
44         %DefaultColor.color_changed.connect(set_setting.bind('dialogic/glossary/default_color'))
45         %DefaultCaseSensitive.toggled.connect(set_setting.bind('dialogic/glossary/default_case_sensitive'))
46
47         %EntryCaseSensitive.icon = get_theme_icon("MatchCase", "EditorIcons")
48
49         %EntryAlternatives.text_changed.connect(_on_entry_alternatives_text_changed)
50
51
52 func set_setting(value: Variant, setting: String)  -> void:
53         ProjectSettings.set_setting(setting, value)
54         ProjectSettings.save()
55
56
57 func _open(_argument: Variant = null) -> void:
58         %DefaultColor.color = ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE)
59         %DefaultCaseSensitive.button_pressed = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true)
60
61         %GlossaryList.clear()
62         var idx := 0
63         for file: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []):
64
65                 if ResourceLoader.exists(file):
66                         %GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileList', 'EditorIcons'))
67                 else:
68                         %GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileDead', 'EditorIcons'))
69
70                 %GlossaryList.set_item_tooltip(idx, file)
71                 idx += 1
72
73         %EntryList.clear()
74
75         if %GlossaryList.item_count != 0:
76                 %GlossaryList.select(0)
77                 _on_GlossaryList_item_selected(0)
78         else:
79                 current_glossary = null
80                 hide_entry_editor()
81
82 ################################################################################
83 ##                                      GLOSSARY LIST
84 ################################################################################
85 func _on_GlossaryList_item_selected(idx: int) -> void:
86         %EntryList.clear()
87         var tooltip_item: String = %GlossaryList.get_item_tooltip(idx)
88
89         if ResourceLoader.exists(tooltip_item):
90                 var glossary_item := load(tooltip_item)
91
92                 if not glossary_item is DialogicGlossary:
93                         return
94
95                 current_glossary = load(tooltip_item)
96
97                 if not current_glossary is DialogicGlossary:
98                         return
99
100                 var entry_idx := 0
101
102                 for entry_key: String in current_glossary.entries.keys():
103                         var entry: Variant = current_glossary.entries.get(entry_key)
104
105                         if entry is String:
106                                 continue
107
108                         # Older glossary entries may not have the name property and the
109                         # alternatives may not be set up as alias entries.
110                         if not entry.has(DialogicGlossary.NAME_PROPERTY):
111                                 entry[DialogicGlossary.NAME_PROPERTY] = entry_key
112                                 var alternatives_array: Array = entry.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
113                                 var alternatives := ",".join(alternatives_array)
114                                 _on_entry_alternatives_text_changed(alternatives)
115                                 ResourceSaver.save(current_glossary)
116
117                         %EntryList.add_item(entry.get(DialogicGlossary.NAME_PROPERTY, str(DialogicGlossary.NAME_PROPERTY)), get_theme_icon("Breakpoint", "EditorIcons"))
118                         var modulate_color: Color = entry.get('color', %DefaultColor.color)
119                         %EntryList.set_item_metadata(entry_idx, entry)
120                         %EntryList.set_item_icon_modulate(entry_idx, modulate_color)
121
122                         entry_idx += 1
123
124         if %EntryList.item_count != 0:
125                 %EntryList.select(0)
126                 _on_EntryList_item_selected(0)
127         else:
128                 hide_entry_editor()
129
130
131 func _on_add_glossary_file_pressed() -> void:
132         find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource')
133
134
135 func create_new_glossary_file(path:String) -> void:
136         var glossary := DialogicGlossary.new()
137         glossary.resource_path = path
138         ResourceSaver.save(glossary, path)
139         load_glossary_file(path)
140
141
142 func _on_load_glossary_file_pressed() -> void:
143         find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource')
144
145
146 func load_glossary_file(path:String) -> void:
147         var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
148
149         if not path in list:
150                 list.append(path)
151                 ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
152                 ProjectSettings.save()
153                 %GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons'))
154
155                 var selected_item_index: int = %GlossaryList.item_count - 1
156
157                 %GlossaryList.set_item_tooltip(selected_item_index, path)
158                 %GlossaryList.select(selected_item_index)
159                 _on_GlossaryList_item_selected(selected_item_index)
160
161
162 func _on_delete_glossary_file_pressed() -> void:
163         var selected_items: PackedInt32Array = %GlossaryList.get_selected_items()
164
165         if not selected_items.is_empty():
166                 var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
167                 var selected_item_index := selected_items[0]
168                 list.remove_at(selected_item_index)
169
170                 ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
171                 ProjectSettings.save()
172
173                 _open()
174
175
176 ################################################################################
177 ##                                      ENTRY LIST
178 ################################################################################
179 func _on_EntryList_item_selected(idx: int) -> void:
180         current_entry_name = %EntryList.get_item_text(idx)
181
182         var entry_info: Dictionary = current_glossary.get_entry(current_entry_name)
183         current_entry = entry_info
184
185         %EntrySettings.show()
186         %EntryName.text = current_entry_name
187         %EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed)
188
189         var alternative_property: Array = entry_info.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
190         var alternatives := ", ".join(alternative_property)
191         %EntryAlternatives.text = alternatives
192
193         %EntryTitle.text = entry_info.get('title', '')
194         %EntryText.text = entry_info.get('text', '')
195         %EntryExtra.text = entry_info.get('extra', '')
196         %EntryEnabled.button_pressed = entry_info.get('enabled', true)
197
198         %EntryColor.color = entry_info.get('color', %DefaultColor.color)
199         %EntryCustomColor.button_pressed = entry_info.has('color')
200         %EntryColor.disabled = !entry_info.has('color')
201
202         _check_entry_alternatives(alternatives)
203         _check_entry_name(current_entry_name, current_entry)
204
205 func _on_add_glossary_entry_pressed() -> void:
206         if !current_glossary:
207                 return
208
209         var entry_count := current_glossary.entries.size() + 1
210         var new_name := "New Entry " + str(entry_count)
211
212         if new_name in current_glossary.entries.keys():
213                 var random_hex_number := str(randi() % 0xFFFFFF)
214                 new_name = new_name + " " + str(random_hex_number)
215
216         var new_glossary := {}
217         new_glossary[DialogicGlossary.NAME_PROPERTY] = new_name
218
219         if not current_glossary.try_add_entry(new_glossary):
220                 print_rich("[color=red]Failed adding '" + new_name + "', exists already.[/color]")
221                 return
222
223         ResourceSaver.save(current_glossary)
224
225         %EntryList.add_item(new_name, get_theme_icon("Breakpoint", "EditorIcons"))
226         var item_count: int = %EntryList.item_count - 1
227
228         %EntryList.set_item_metadata(item_count, new_name)
229         %EntryList.set_item_icon_modulate(item_count, %DefaultColor.color)
230         %EntryList.select(item_count)
231
232         _on_EntryList_item_selected(item_count)
233
234         %EntryList.ensure_current_is_visible()
235         %EntryName.grab_focus()
236
237
238 func _on_delete_glossary_entry_pressed() -> void:
239         var selected_items: Array = %EntryList.get_selected_items()
240
241         if not selected_items.is_empty():
242                 var selected_item_index: int = selected_items[0]
243
244                 if not current_glossary == null:
245                         current_glossary.remove_entry(current_entry_name)
246                         ResourceSaver.save(current_glossary)
247
248                         %EntryList.remove_item(selected_item_index)
249                         var entries_count: int = %EntryList.item_count
250
251                         if entries_count > 0:
252                                 var previous_item_index := selected_item_index - 1
253                                 %EntryList.select(previous_item_index)
254
255
256
257 func _on_entry_search_text_changed(new_text: String) -> void:
258         if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(%EntryList.get_selected_items()[0]).to_lower():
259                 return
260
261         for i: int in %EntryList.item_count:
262
263                 if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(i).to_lower():
264                         %EntryList.select(i)
265                         _on_EntryList_item_selected(i)
266                         %EntryList.ensure_current_is_visible()
267
268
269 ################################################################################
270 ##                                      ENTRY EDITOR
271 ################################################################################
272 func hide_entry_editor() -> void:
273         %EntrySettings.hide()
274
275
276 func _update_alias_entries(old_alias_value_key: String, new_alias_value_key: String) -> void:
277         for entry_key: String in current_glossary.entries.keys():
278
279                 var entry_value: Variant = current_glossary.entries.get(entry_key)
280
281                 if not entry_value is String:
282                         continue
283
284                 if not entry_value == old_alias_value_key:
285                         continue
286
287                 current_glossary.entries[entry_key] = new_alias_value_key
288
289
290 ## Checks if the [param entry_name] is already used as a key for another entry
291 ## and returns true if it doesn't.
292 ## The [param entry] will be used to check if found entry uses the same
293 ## reference in memory.
294 func _check_entry_name(entry_name: String, entry: Dictionary) -> bool:
295         var selected_item: int = %EntryList.get_selected_items()[0]
296         var raised_error: bool = false
297
298         var entry_assigned: Variant = current_glossary.entries.get(entry_name, {})
299
300         # Alternative entry uses the entry name already.
301         if entry_assigned is String:
302                 raised_error = true
303
304         if entry_assigned is Dictionary and not entry_assigned.is_empty():
305                 var entry_name_assigned: String = entry_assigned.get(DialogicGlossary.NAME_PROPERTY, "")
306
307                 # Another entry uses the entry name already.
308                 if not entry_name_assigned == entry_name:
309                         raised_error = true
310
311                 # Not the same memory reference.
312                 if not entry == entry_assigned:
313                         raised_error = true
314
315         if raised_error:
316                 %EntryList.set_item_custom_bg_color(selected_item,
317                                 get_theme_color("warning_color", "Editor").darkened(0.8))
318                 %EntryName.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
319                 %EntryName.right_icon = get_theme_icon("StatusError", "EditorIcons")
320
321                 return false
322
323         else:
324                 %EntryName.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
325                 %EntryName.add_theme_color_override("caret_color", get_theme_color("font_color", "Editor"))
326                 %EntryName.right_icon = null
327                 %EntryList.set_item_custom_bg_color(
328                         selected_item,
329                         Color.TRANSPARENT
330                 )
331
332         return true
333
334
335 func _on_entry_name_text_changed(new_name: String) -> void:
336         new_name = new_name.strip_edges()
337
338         if current_entry_name != new_name:
339                 var selected_item: int = %EntryList.get_selected_items()[0]
340
341                 if not _check_entry_name(new_name, current_entry):
342                         return
343
344                 print_rich("[color=green]Renaming entry '" + current_entry_name + "'' to '" + new_name + "'[/color]")
345
346                 _update_alias_entries(current_entry_name, new_name)
347
348                 current_glossary.replace_entry_key(current_entry_name, new_name)
349
350                 %EntryList.set_item_text(selected_item, new_name)
351                 %EntryList.set_item_metadata(selected_item, new_name)
352                 ResourceSaver.save(current_glossary)
353                 current_entry_name = new_name
354
355
356 func _on_entry_case_sensitive_toggled(button_pressed: bool) -> void:
357         current_glossary.get_entry(current_entry_name)['case_sensitive'] = button_pressed
358         ResourceSaver.save(current_glossary)
359
360
361 ## Checks if the [param new_alternatives] has any alternatives that are already
362 ## used as a key for another entry and returns true if it doesn't.
363 func _can_change_alternative(new_alternatives: String) -> bool:
364         for alternative: String in new_alternatives.split(',', false):
365                 var stripped_alternative := alternative.strip_edges()
366
367                 var value: Variant = current_glossary.entries.get(stripped_alternative, null)
368
369                 if value == null:
370                         continue
371
372                 if value is String:
373                         value = current_glossary.entries.get(value, null)
374
375                 var value_name: String = value[DialogicGlossary.NAME_PROPERTY]
376
377                 if not current_entry_name == value_name:
378                         return false
379
380         return true
381
382
383 ## Checks if [entry_alternatives] has any alternatives that are already
384 ## used by any entry and returns true if it doesn't.
385 ## If false, it will set the alternatives text field to a warning color and
386 ## set an icon.
387 ## If true, the alternatives text field will be set to the default color and
388 ## the icon will be removed.
389 func _check_entry_alternatives(entry_alternatives: String) -> bool:
390
391         if not _can_change_alternative(entry_alternatives):
392                 %EntryAlternatives.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
393                 %EntryAlternatives.right_icon = get_theme_icon("StatusError", "EditorIcons")
394                 return false
395
396         else:
397                 %EntryAlternatives.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
398                 %EntryAlternatives.right_icon = null
399
400         return true
401
402
403 ## The [param new_alternatives] is a passed as a string of comma separated
404 ## values form the Dialogic editor.
405 ##
406 ## Saves the glossary resource file.
407 func _on_entry_alternatives_text_changed(new_alternatives: String) -> void:
408         var current_alternatives: Array = current_glossary.get_entry(current_entry_name).get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
409
410         if not _check_entry_alternatives(new_alternatives):
411                 return
412
413         for current_alternative: String in current_alternatives:
414                 current_glossary._remove_entry_alias(current_alternative)
415
416         var alternatives := []
417
418         for new_alternative: String in new_alternatives.split(',', false):
419                 var stripped_alternative := new_alternative.strip_edges()
420                 alternatives.append(stripped_alternative)
421                 current_glossary._add_entry_key_alias(current_entry_name, stripped_alternative)
422
423         current_glossary.get_entry(current_entry_name)[DialogicGlossary.ALTERNATIVE_PROPERTY] = alternatives
424         ResourceSaver.save(current_glossary)
425
426
427 func _on_entry_title_text_changed(new_text:String) -> void:
428         current_glossary.get_entry(current_entry_name)['title'] = new_text
429         ResourceSaver.save(current_glossary)
430
431
432 func _on_entry_text_text_changed() -> void:
433         current_glossary.get_entry(current_entry_name)['text'] = %EntryText.text
434         ResourceSaver.save(current_glossary)
435
436
437 func _on_entry_extra_text_changed() -> void:
438         current_glossary.get_entry(current_entry_name)['extra'] = %EntryExtra.text
439         ResourceSaver.save(current_glossary)
440
441
442 func _on_entry_enabled_toggled(button_pressed:bool) -> void:
443         current_glossary.get_entry(current_entry_name)['enabled'] = button_pressed
444         ResourceSaver.save(current_glossary)
445
446
447 func _on_entry_custom_color_toggled(button_pressed:bool) -> void:
448         %EntryColor.disabled = !button_pressed
449
450         if !button_pressed:
451                 current_glossary.get_entry(current_entry_name).erase('color')
452                 %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %DefaultColor.color)
453         else:
454                 current_glossary.get_entry(current_entry_name)['color'] = %EntryColor.color
455                 %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %EntryColor.color)
456
457
458 func _on_entry_color_color_changed(color:Color) -> void:
459         current_glossary.get_entry(current_entry_name)['color'] = color
460         %EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], color)
461         ResourceSaver.save(current_glossary)