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.
462 lines
16 KiB
462 lines
16 KiB
6 months ago
|
@tool
|
||
|
extends DialogicEditor
|
||
|
|
||
|
var current_glossary: DialogicGlossary = null
|
||
|
var current_entry_name := ""
|
||
|
var current_entry := {}
|
||
|
|
||
|
################################################################################
|
||
|
## BASICS
|
||
|
################################################################################
|
||
|
|
||
|
func _get_title() -> String:
|
||
|
return "Glossary"
|
||
|
|
||
|
|
||
|
func _get_icon() -> Texture:
|
||
|
var base_directory: String = self.get_script().get_path().get_base_dir()
|
||
|
var icon_path := base_directory + "/icon.svg"
|
||
|
return load(icon_path)
|
||
|
|
||
|
|
||
|
func _register() -> void:
|
||
|
editors_manager.register_simple_editor(self)
|
||
|
alternative_text = "Create and edit glossaries."
|
||
|
|
||
|
|
||
|
func _ready() -> void:
|
||
|
var add_glossary_icon_path: String = self.get_script().get_path().get_base_dir() + "/add-glossary.svg"
|
||
|
var add_glossary_icon := load(add_glossary_icon_path)
|
||
|
%AddGlossaryFile.icon = add_glossary_icon
|
||
|
|
||
|
%LoadGlossaryFile.icon = get_theme_icon('Folder', 'EditorIcons')
|
||
|
%DeleteGlossaryFile.icon = get_theme_icon('Remove', 'EditorIcons')
|
||
|
%DeleteGlossaryEntry.icon = get_theme_icon('Remove', 'EditorIcons')
|
||
|
|
||
|
%DeleteGlossaryFile.pressed.connect(_on_delete_glossary_file_pressed)
|
||
|
|
||
|
%AddGlossaryEntry.icon = get_theme_icon('Add', 'EditorIcons')
|
||
|
%EntrySearch.right_icon = get_theme_icon('Search', 'EditorIcons')
|
||
|
|
||
|
%GlossaryList.item_selected.connect(_on_GlossaryList_item_selected)
|
||
|
%EntryList.item_selected.connect(_on_EntryList_item_selected)
|
||
|
|
||
|
%DefaultColor.color_changed.connect(set_setting.bind('dialogic/glossary/default_color'))
|
||
|
%DefaultCaseSensitive.toggled.connect(set_setting.bind('dialogic/glossary/default_case_sensitive'))
|
||
|
|
||
|
%EntryCaseSensitive.icon = get_theme_icon("MatchCase", "EditorIcons")
|
||
|
|
||
|
%EntryAlternatives.text_changed.connect(_on_entry_alternatives_text_changed)
|
||
|
|
||
|
|
||
|
func set_setting(value: Variant, setting: String) -> void:
|
||
|
ProjectSettings.set_setting(setting, value)
|
||
|
ProjectSettings.save()
|
||
|
|
||
|
|
||
|
func _open(_argument: Variant = null) -> void:
|
||
|
%DefaultColor.color = ProjectSettings.get_setting('dialogic/glossary/default_color', Color.POWDER_BLUE)
|
||
|
%DefaultCaseSensitive.button_pressed = ProjectSettings.get_setting('dialogic/glossary/default_case_sensitive', true)
|
||
|
|
||
|
%GlossaryList.clear()
|
||
|
var idx := 0
|
||
|
for file: String in ProjectSettings.get_setting('dialogic/glossary/glossary_files', []):
|
||
|
|
||
|
if ResourceLoader.exists(file):
|
||
|
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileList', 'EditorIcons'))
|
||
|
else:
|
||
|
%GlossaryList.add_item(DialogicUtil.pretty_name(file), get_theme_icon('FileDead', 'EditorIcons'))
|
||
|
|
||
|
%GlossaryList.set_item_tooltip(idx, file)
|
||
|
idx += 1
|
||
|
|
||
|
%EntryList.clear()
|
||
|
|
||
|
if %GlossaryList.item_count != 0:
|
||
|
%GlossaryList.select(0)
|
||
|
_on_GlossaryList_item_selected(0)
|
||
|
else:
|
||
|
current_glossary = null
|
||
|
hide_entry_editor()
|
||
|
|
||
|
################################################################################
|
||
|
## GLOSSARY LIST
|
||
|
################################################################################
|
||
|
func _on_GlossaryList_item_selected(idx: int) -> void:
|
||
|
%EntryList.clear()
|
||
|
var tooltip_item: String = %GlossaryList.get_item_tooltip(idx)
|
||
|
|
||
|
if ResourceLoader.exists(tooltip_item):
|
||
|
var glossary_item := load(tooltip_item)
|
||
|
|
||
|
if not glossary_item is DialogicGlossary:
|
||
|
return
|
||
|
|
||
|
current_glossary = load(tooltip_item)
|
||
|
|
||
|
if not current_glossary is DialogicGlossary:
|
||
|
return
|
||
|
|
||
|
var entry_idx := 0
|
||
|
|
||
|
for entry_key: String in current_glossary.entries.keys():
|
||
|
var entry: Variant = current_glossary.entries.get(entry_key)
|
||
|
|
||
|
if entry is String:
|
||
|
continue
|
||
|
|
||
|
# Older glossary entries may not have the name property and the
|
||
|
# alternatives may not be set up as alias entries.
|
||
|
if not entry.has(DialogicGlossary.NAME_PROPERTY):
|
||
|
entry[DialogicGlossary.NAME_PROPERTY] = entry_key
|
||
|
var alternatives_array: Array = entry.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
|
||
|
var alternatives := ",".join(alternatives_array)
|
||
|
_on_entry_alternatives_text_changed(alternatives)
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
%EntryList.add_item(entry.get(DialogicGlossary.NAME_PROPERTY, str(DialogicGlossary.NAME_PROPERTY)), get_theme_icon("Breakpoint", "EditorIcons"))
|
||
|
var modulate_color: Color = entry.get('color', %DefaultColor.color)
|
||
|
%EntryList.set_item_metadata(entry_idx, entry)
|
||
|
%EntryList.set_item_icon_modulate(entry_idx, modulate_color)
|
||
|
|
||
|
entry_idx += 1
|
||
|
|
||
|
if %EntryList.item_count != 0:
|
||
|
%EntryList.select(0)
|
||
|
_on_EntryList_item_selected(0)
|
||
|
else:
|
||
|
hide_entry_editor()
|
||
|
|
||
|
|
||
|
func _on_add_glossary_file_pressed() -> void:
|
||
|
find_parent('EditorView').godot_file_dialog(create_new_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_SAVE_FILE, 'Create new glossary resource')
|
||
|
|
||
|
|
||
|
func create_new_glossary_file(path:String) -> void:
|
||
|
var glossary := DialogicGlossary.new()
|
||
|
glossary.resource_path = path
|
||
|
ResourceSaver.save(glossary, path)
|
||
|
load_glossary_file(path)
|
||
|
|
||
|
|
||
|
func _on_load_glossary_file_pressed() -> void:
|
||
|
find_parent('EditorView').godot_file_dialog(load_glossary_file, '*.tres', EditorFileDialog.FILE_MODE_OPEN_FILE, 'Select glossary resource')
|
||
|
|
||
|
|
||
|
func load_glossary_file(path:String) -> void:
|
||
|
var list :Array= ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
|
||
|
|
||
|
if not path in list:
|
||
|
list.append(path)
|
||
|
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
|
||
|
ProjectSettings.save()
|
||
|
%GlossaryList.add_item(DialogicUtil.pretty_name(path), get_theme_icon('FileList', 'EditorIcons'))
|
||
|
|
||
|
var selected_item_index: int = %GlossaryList.item_count - 1
|
||
|
|
||
|
%GlossaryList.set_item_tooltip(selected_item_index, path)
|
||
|
%GlossaryList.select(selected_item_index)
|
||
|
_on_GlossaryList_item_selected(selected_item_index)
|
||
|
|
||
|
|
||
|
func _on_delete_glossary_file_pressed() -> void:
|
||
|
var selected_items: PackedInt32Array = %GlossaryList.get_selected_items()
|
||
|
|
||
|
if not selected_items.is_empty():
|
||
|
var list: Array = ProjectSettings.get_setting('dialogic/glossary/glossary_files', [])
|
||
|
var selected_item_index := selected_items[0]
|
||
|
list.remove_at(selected_item_index)
|
||
|
|
||
|
ProjectSettings.set_setting('dialogic/glossary/glossary_files', list)
|
||
|
ProjectSettings.save()
|
||
|
|
||
|
_open()
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
## ENTRY LIST
|
||
|
################################################################################
|
||
|
func _on_EntryList_item_selected(idx: int) -> void:
|
||
|
current_entry_name = %EntryList.get_item_text(idx)
|
||
|
|
||
|
var entry_info: Dictionary = current_glossary.get_entry(current_entry_name)
|
||
|
current_entry = entry_info
|
||
|
|
||
|
%EntrySettings.show()
|
||
|
%EntryName.text = current_entry_name
|
||
|
%EntryCaseSensitive.button_pressed = entry_info.get('case_sensitive', %DefaultCaseSensitive.button_pressed)
|
||
|
|
||
|
var alternative_property: Array = entry_info.get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
|
||
|
var alternatives := ", ".join(alternative_property)
|
||
|
%EntryAlternatives.text = alternatives
|
||
|
|
||
|
%EntryTitle.text = entry_info.get('title', '')
|
||
|
%EntryText.text = entry_info.get('text', '')
|
||
|
%EntryExtra.text = entry_info.get('extra', '')
|
||
|
%EntryEnabled.button_pressed = entry_info.get('enabled', true)
|
||
|
|
||
|
%EntryColor.color = entry_info.get('color', %DefaultColor.color)
|
||
|
%EntryCustomColor.button_pressed = entry_info.has('color')
|
||
|
%EntryColor.disabled = !entry_info.has('color')
|
||
|
|
||
|
_check_entry_alternatives(alternatives)
|
||
|
_check_entry_name(current_entry_name, current_entry)
|
||
|
|
||
|
func _on_add_glossary_entry_pressed() -> void:
|
||
|
if !current_glossary:
|
||
|
return
|
||
|
|
||
|
var entry_count := current_glossary.entries.size() + 1
|
||
|
var new_name := "New Entry " + str(entry_count)
|
||
|
|
||
|
if new_name in current_glossary.entries.keys():
|
||
|
var random_hex_number := str(randi() % 0xFFFFFF)
|
||
|
new_name = new_name + " " + str(random_hex_number)
|
||
|
|
||
|
var new_glossary := {}
|
||
|
new_glossary[DialogicGlossary.NAME_PROPERTY] = new_name
|
||
|
|
||
|
if not current_glossary.try_add_entry(new_glossary):
|
||
|
print_rich("[color=red]Failed adding '" + new_name + "', exists already.[/color]")
|
||
|
return
|
||
|
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
%EntryList.add_item(new_name, get_theme_icon("Breakpoint", "EditorIcons"))
|
||
|
var item_count: int = %EntryList.item_count - 1
|
||
|
|
||
|
%EntryList.set_item_metadata(item_count, new_name)
|
||
|
%EntryList.set_item_icon_modulate(item_count, %DefaultColor.color)
|
||
|
%EntryList.select(item_count)
|
||
|
|
||
|
_on_EntryList_item_selected(item_count)
|
||
|
|
||
|
%EntryList.ensure_current_is_visible()
|
||
|
%EntryName.grab_focus()
|
||
|
|
||
|
|
||
|
func _on_delete_glossary_entry_pressed() -> void:
|
||
|
var selected_items: Array = %EntryList.get_selected_items()
|
||
|
|
||
|
if not selected_items.is_empty():
|
||
|
var selected_item_index: int = selected_items[0]
|
||
|
|
||
|
if not current_glossary == null:
|
||
|
current_glossary.remove_entry(current_entry_name)
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
%EntryList.remove_item(selected_item_index)
|
||
|
var entries_count: int = %EntryList.item_count
|
||
|
|
||
|
if entries_count > 0:
|
||
|
var previous_item_index := selected_item_index - 1
|
||
|
%EntryList.select(previous_item_index)
|
||
|
|
||
|
|
||
|
|
||
|
func _on_entry_search_text_changed(new_text: String) -> void:
|
||
|
if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(%EntryList.get_selected_items()[0]).to_lower():
|
||
|
return
|
||
|
|
||
|
for i: int in %EntryList.item_count:
|
||
|
|
||
|
if new_text.is_empty() or new_text.to_lower() in %EntryList.get_item_text(i).to_lower():
|
||
|
%EntryList.select(i)
|
||
|
_on_EntryList_item_selected(i)
|
||
|
%EntryList.ensure_current_is_visible()
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
## ENTRY EDITOR
|
||
|
################################################################################
|
||
|
func hide_entry_editor() -> void:
|
||
|
%EntrySettings.hide()
|
||
|
|
||
|
|
||
|
func _update_alias_entries(old_alias_value_key: String, new_alias_value_key: String) -> void:
|
||
|
for entry_key: String in current_glossary.entries.keys():
|
||
|
|
||
|
var entry_value: Variant = current_glossary.entries.get(entry_key)
|
||
|
|
||
|
if not entry_value is String:
|
||
|
continue
|
||
|
|
||
|
if not entry_value == old_alias_value_key:
|
||
|
continue
|
||
|
|
||
|
current_glossary.entries[entry_key] = new_alias_value_key
|
||
|
|
||
|
|
||
|
## Checks if the [param entry_name] is already used as a key for another entry
|
||
|
## and returns true if it doesn't.
|
||
|
## The [param entry] will be used to check if found entry uses the same
|
||
|
## reference in memory.
|
||
|
func _check_entry_name(entry_name: String, entry: Dictionary) -> bool:
|
||
|
var selected_item: int = %EntryList.get_selected_items()[0]
|
||
|
var raised_error: bool = false
|
||
|
|
||
|
var entry_assigned: Variant = current_glossary.entries.get(entry_name, {})
|
||
|
|
||
|
# Alternative entry uses the entry name already.
|
||
|
if entry_assigned is String:
|
||
|
raised_error = true
|
||
|
|
||
|
if entry_assigned is Dictionary and not entry_assigned.is_empty():
|
||
|
var entry_name_assigned: String = entry_assigned.get(DialogicGlossary.NAME_PROPERTY, "")
|
||
|
|
||
|
# Another entry uses the entry name already.
|
||
|
if not entry_name_assigned == entry_name:
|
||
|
raised_error = true
|
||
|
|
||
|
# Not the same memory reference.
|
||
|
if not entry == entry_assigned:
|
||
|
raised_error = true
|
||
|
|
||
|
if raised_error:
|
||
|
%EntryList.set_item_custom_bg_color(selected_item,
|
||
|
get_theme_color("warning_color", "Editor").darkened(0.8))
|
||
|
%EntryName.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
|
||
|
%EntryName.right_icon = get_theme_icon("StatusError", "EditorIcons")
|
||
|
|
||
|
return false
|
||
|
|
||
|
else:
|
||
|
%EntryName.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
|
||
|
%EntryName.add_theme_color_override("caret_color", get_theme_color("font_color", "Editor"))
|
||
|
%EntryName.right_icon = null
|
||
|
%EntryList.set_item_custom_bg_color(
|
||
|
selected_item,
|
||
|
Color.TRANSPARENT
|
||
|
)
|
||
|
|
||
|
return true
|
||
|
|
||
|
|
||
|
func _on_entry_name_text_changed(new_name: String) -> void:
|
||
|
new_name = new_name.strip_edges()
|
||
|
|
||
|
if current_entry_name != new_name:
|
||
|
var selected_item: int = %EntryList.get_selected_items()[0]
|
||
|
|
||
|
if not _check_entry_name(new_name, current_entry):
|
||
|
return
|
||
|
|
||
|
print_rich("[color=green]Renaming entry '" + current_entry_name + "'' to '" + new_name + "'[/color]")
|
||
|
|
||
|
_update_alias_entries(current_entry_name, new_name)
|
||
|
|
||
|
current_glossary.replace_entry_key(current_entry_name, new_name)
|
||
|
|
||
|
%EntryList.set_item_text(selected_item, new_name)
|
||
|
%EntryList.set_item_metadata(selected_item, new_name)
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
current_entry_name = new_name
|
||
|
|
||
|
|
||
|
func _on_entry_case_sensitive_toggled(button_pressed: bool) -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['case_sensitive'] = button_pressed
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
## Checks if the [param new_alternatives] has any alternatives that are already
|
||
|
## used as a key for another entry and returns true if it doesn't.
|
||
|
func _can_change_alternative(new_alternatives: String) -> bool:
|
||
|
for alternative: String in new_alternatives.split(',', false):
|
||
|
var stripped_alternative := alternative.strip_edges()
|
||
|
|
||
|
var value: Variant = current_glossary.entries.get(stripped_alternative, null)
|
||
|
|
||
|
if value == null:
|
||
|
continue
|
||
|
|
||
|
if value is String:
|
||
|
value = current_glossary.entries.get(value, null)
|
||
|
|
||
|
var value_name: String = value[DialogicGlossary.NAME_PROPERTY]
|
||
|
|
||
|
if not current_entry_name == value_name:
|
||
|
return false
|
||
|
|
||
|
return true
|
||
|
|
||
|
|
||
|
## Checks if [entry_alternatives] has any alternatives that are already
|
||
|
## used by any entry and returns true if it doesn't.
|
||
|
## If false, it will set the alternatives text field to a warning color and
|
||
|
## set an icon.
|
||
|
## If true, the alternatives text field will be set to the default color and
|
||
|
## the icon will be removed.
|
||
|
func _check_entry_alternatives(entry_alternatives: String) -> bool:
|
||
|
|
||
|
if not _can_change_alternative(entry_alternatives):
|
||
|
%EntryAlternatives.add_theme_color_override("font_color", get_theme_color("warning_color", "Editor"))
|
||
|
%EntryAlternatives.right_icon = get_theme_icon("StatusError", "EditorIcons")
|
||
|
return false
|
||
|
|
||
|
else:
|
||
|
%EntryAlternatives.add_theme_color_override("font_color", get_theme_color("font_color", "Editor"))
|
||
|
%EntryAlternatives.right_icon = null
|
||
|
|
||
|
return true
|
||
|
|
||
|
|
||
|
## The [param new_alternatives] is a passed as a string of comma separated
|
||
|
## values form the Dialogic editor.
|
||
|
##
|
||
|
## Saves the glossary resource file.
|
||
|
func _on_entry_alternatives_text_changed(new_alternatives: String) -> void:
|
||
|
var current_alternatives: Array = current_glossary.get_entry(current_entry_name).get(DialogicGlossary.ALTERNATIVE_PROPERTY, [])
|
||
|
|
||
|
if not _check_entry_alternatives(new_alternatives):
|
||
|
return
|
||
|
|
||
|
for current_alternative: String in current_alternatives:
|
||
|
current_glossary._remove_entry_alias(current_alternative)
|
||
|
|
||
|
var alternatives := []
|
||
|
|
||
|
for new_alternative: String in new_alternatives.split(',', false):
|
||
|
var stripped_alternative := new_alternative.strip_edges()
|
||
|
alternatives.append(stripped_alternative)
|
||
|
current_glossary._add_entry_key_alias(current_entry_name, stripped_alternative)
|
||
|
|
||
|
current_glossary.get_entry(current_entry_name)[DialogicGlossary.ALTERNATIVE_PROPERTY] = alternatives
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
func _on_entry_title_text_changed(new_text:String) -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['title'] = new_text
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
func _on_entry_text_text_changed() -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['text'] = %EntryText.text
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
func _on_entry_extra_text_changed() -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['extra'] = %EntryExtra.text
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
func _on_entry_enabled_toggled(button_pressed:bool) -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['enabled'] = button_pressed
|
||
|
ResourceSaver.save(current_glossary)
|
||
|
|
||
|
|
||
|
func _on_entry_custom_color_toggled(button_pressed:bool) -> void:
|
||
|
%EntryColor.disabled = !button_pressed
|
||
|
|
||
|
if !button_pressed:
|
||
|
current_glossary.get_entry(current_entry_name).erase('color')
|
||
|
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %DefaultColor.color)
|
||
|
else:
|
||
|
current_glossary.get_entry(current_entry_name)['color'] = %EntryColor.color
|
||
|
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], %EntryColor.color)
|
||
|
|
||
|
|
||
|
func _on_entry_color_color_changed(color:Color) -> void:
|
||
|
current_glossary.get_entry(current_entry_name)['color'] = color
|
||
|
%EntryList.set_item_icon_modulate(%EntryList.get_selected_items()[0], color)
|
||
|
ResourceSaver.save(current_glossary)
|