This repository has been archived on 2024-04-19. You can view files and clone it, but cannot push or open issues or pull requests.
Jessist/addons/dialogue_manager/views/main_view.gd
2022-06-18 13:05:48 +02:00

490 lines
15 KiB
GDScript

tool
extends Control
const DialogueResource = preload("res://addons/dialogue_manager/dialogue_resource.gd")
const DialogueConstants = preload("res://addons/dialogue_manager/constants.gd")
onready var settings := $Settings
onready var parser := $Parser
onready var parse_timeout := $ParseTimeout
onready var update_checker := $UpdateChecker
onready var file_label := $Margin/VBox/Toolbar/FileLabel
onready var open_button := $Margin/VBox/Toolbar/OpenButton
onready var content := $Margin/VBox/Content
onready var title_list := $Margin/VBox/Content/VBox/TitleList
onready var error_list := $Margin/VBox/Content/VBox/ErrorList
onready var search_toolbar := $Margin/VBox/Content/VBox2/SearchToolbar
onready var editor := $Margin/VBox/Content/VBox2/CodeEditor
onready var new_dialogue_dialog := $NewDialogueDialog
onready var open_dialogue_dialog := $OpenDialogueDialog
onready var invalid_dialogue_dialog := $InvalidDialogueDialog
onready var settings_dialog := $SettingsDialog
onready var insert_menu := $Margin/VBox/Toolbar/InsertMenu
onready var translations_menu := $Margin/VBox/Toolbar/TranslationsMenu
onready var save_translations_dialog := $SaveTranslationsDialog
onready var update_button := $Margin/VBox/Toolbar/UpdateButton
onready var error_button := $Margin/VBox/Toolbar/ErrorButton
onready var run_node_button := $Margin/VBox/Toolbar/RunButton
onready var search_button := $Margin/VBox/Toolbar/SearchButton
var plugin
var current_resource: DialogueResource
var has_changed: bool = false
var recent_resources: Array
func _ready() -> void:
# Hide the editor until we open something
set_resource(null)
# Check for updates
update_checker.check_for_updates()
update_button.visible = false
update_button.add_color_override("font_color", get_color("success_color", "Editor"))
# Set up the button icons
$Margin/VBox/Toolbar/NewButton.text = ""
$Margin/VBox/Toolbar/NewButton.icon = get_icon("New", "EditorIcons")
open_button.text = ""
open_button.icon = get_icon("Load", "EditorIcons")
$Margin/VBox/Toolbar/SettingsButton.text = ""
$Margin/VBox/Toolbar/SettingsButton.icon = get_icon("Tools", "EditorIcons")
error_button.text = ""
error_button.icon = get_icon("Debug", "EditorIcons")
run_node_button.text = ""
run_node_button.icon = get_icon("PlayScene", "EditorIcons")
search_button.icon = get_icon("Search", "EditorIcons")
$Margin/VBox/Toolbar/TranslationsMenu.icon = get_icon("Translation", "EditorIcons")
$Margin/VBox/Toolbar/HelpButton.icon = get_icon("Help", "EditorIcons")
insert_menu.icon = get_icon("RichTextEffect", "EditorIcons")
var popup = insert_menu.get_popup()
popup.set_item_icon(0, get_icon("RichTextEffect", "EditorIcons"))
popup.set_item_icon(1, get_icon("RichTextEffect", "EditorIcons"))
popup.set_item_icon(3, get_icon("Time", "EditorIcons"))
popup.set_item_icon(4, get_icon("ViewportSpeed", "EditorIcons"))
popup.set_item_icon(5, get_icon("DebugNext", "EditorIcons"))
popup = translations_menu.get_popup()
popup.set_item_icon(0, get_icon("Translation", "EditorIcons"))
popup.set_item_icon(1, get_icon("FileList", "EditorIcons"))
search_toolbar.visible = false
# Get version number
var config = ConfigFile.new()
var err = config.load("res://addons/dialogue_manager/plugin.cfg")
if err == OK:
$Margin/VBox/Toolbar/VersionLabel.text = "v" + config.get_value("plugin", "version")
file_label.icon = get_icon("Filesystem", "EditorIcons")
insert_menu.get_popup().connect("id_pressed", self, "_on_insert_menu_id_pressed")
translations_menu.get_popup().connect("id_pressed", self, "_on_translation_menu_id_pressed")
recent_resources = settings.get_editor_value("recent_resources", [])
build_open_menu()
editor.wrap_enabled = settings.get_editor_value("wrap_lines", false)
func apply_changes() -> void:
if is_instance_valid(editor) and current_resource != null:
current_resource.set("raw_text", editor.text)
ResourceSaver.save(current_resource.resource_path, current_resource)
parse(true)
### Helpers
func build_open_menu() -> void:
var menu = open_button.get_popup()
menu.clear()
menu.add_icon_item(get_icon("Load", "EditorIcons"), "Open...")
menu.add_separator()
if recent_resources.size() == 0:
menu.add_item("No recent files")
menu.set_item_disabled(2, true)
else:
for path in recent_resources:
menu.add_icon_item(get_icon("File", "EditorIcons"), path)
menu.add_separator()
menu.add_item("Clear recent files")
if menu.is_connected("index_pressed", self, "_on_open_menu_index_pressed"):
menu.disconnect("index_pressed", self, "_on_open_menu_index_pressed")
menu.connect("index_pressed", self, "_on_open_menu_index_pressed")
func set_resource(value: DialogueResource) -> void:
current_resource = value
if current_resource:
file_label.text = get_nice_file(current_resource.resource_path)
file_label.visible = true
editor.text = current_resource.raw_text
editor.clear_undo_history()
content.visible = true
error_button.disabled = false
run_node_button.disabled = false
search_button.disabled = false
insert_menu.disabled = false
translations_menu.disabled = false
_on_CodeEditor_text_changed()
has_changed = false
else:
content.visible = false
file_label.visible = false
error_button.disabled = true
run_node_button.disabled = true
search_button.disabled = true
insert_menu.disabled = true
translations_menu.disabled = true
func get_nice_file(file: String) -> String:
var bits = file.replace("res://", "").split("/")
if bits.size() == 1:
return bits[0]
else:
return "%s/%s" % [bits[bits.size() - 2], bits[bits.size() - 1]]
func open_resource(resource: DialogueResource) -> void:
apply_upgrades(resource)
set_resource(resource)
# Add this to our list of recent resources
if resource.resource_path in recent_resources:
recent_resources.erase(resource.resource_path)
recent_resources.insert(0, resource.resource_path)
settings.set_editor_value("recent_resources", recent_resources)
build_open_menu()
parse(true)
func open_resource_from_path(path: String) -> void:
var resource = load(path)
if resource is DialogueResource:
open_resource(resource)
else:
invalid_dialogue_dialog.popup_centered()
func apply_upgrades(resource: DialogueResource) -> void:
if resource == null: return
if not resource is DialogueResource: return
var lines = resource.raw_text.split("\n")
for i in range(0, lines.size()):
var line: String = lines[i]
if resource.syntax_version == 0:
if line.begins_with("# "):
line = "~ " + line.substr(2).replace(" ", "_")
line = line.replace("// ", "# ")
if "goto #" in line:
var index = line.find("goto # ")
line = line.substr(0, index) + "=> " + line.substr(index + 7).replace(" ", "_")
lines[i] = line
resource.set("syntax_version", DialogueConstants.SYNTAX_VERSION)
resource.set("raw_text", lines.join("\n"))
func parse(force_show_errors: bool = false) -> void:
if current_resource == null: return
if not has_changed and not force_show_errors: return
var result = parser.parse(editor.text)
if settings.get_editor_value("store_compiler_results", true):
current_resource.set("titles", result.titles)
current_resource.set("lines", result.lines)
current_resource.set("errors", result.errors)
else:
current_resource.set("titles", {})
current_resource.set("lines", {})
current_resource.set("errors", [])
ResourceSaver.save(current_resource.resource_path, current_resource)
has_changed = false
if force_show_errors or settings.get_editor_value("check_for_errors") or error_list.errors.size() > 0:
error_list.errors = result.errors
for line_number in range(0, editor.get_line_count() - 1):
editor.set_line_as_bookmark(line_number, false)
for error in result.errors:
if error.get("line") == line_number:
editor.set_line_as_bookmark(line_number, true)
func generate_translations_keys() -> void:
randomize()
seed(OS.get_unix_time())
var lines: PoolStringArray = editor.text.split("\n")
var key_regex = RegEx.new()
key_regex.compile("\\[TR:(?<key>.*?)\\]")
# Make list of known keys
var known_keys = {}
for i in range(0, lines.size()):
var line = lines[i]
var found = key_regex.search(line)
if found:
var text = ""
var l = line.replace(found.strings[0], "").strip_edges().strip_edges()
if l.begins_with("- "):
text = parser.extract_response(l)
elif ":" in l:
text = l.split(":")[1]
else:
text = l
known_keys[found.strings[found.names.get("key")]] = text
# Add in any that are missing
for i in lines.size():
var line = lines[i]
var l = line.strip_edges()
if l == "" or l.begins_with("#"): continue
if l.begins_with("if ") or l.begins_with("elif ") or l.begins_with("else") or l.begins_with("endif"): continue
if l.begins_with("~ "): continue
if l.begins_with("do ") or l.begins_with("set "): continue
if l.begins_with("=>"): continue
if "[TR:" in line: continue
var key = "t" + str(randi() % 1000000).sha1_text().substr(0, 10)
while key in known_keys:
key = "t" + str(randi() % 1000000).sha1_text().substr(0, 10)
# See if identical text already has a key
var text = ""
if l.begins_with("- "):
text = parser.extract_response(l)
else:
text = l.substr(l.find(":") + 1)
var index = known_keys.values().find(text)
if index > -1:
key = known_keys.keys()[index]
lines[i] = line.replace(text, text + " [TR:%s]" % key)
known_keys[key] = text
editor.text = lines.join("\n")
_on_CodeEditor_text_changed()
func save_translations(path: String) -> void:
var file = File.new()
# If the file exists, open it first and work out which keys are already in it
var existing_csv = {}
var commas = []
if file.file_exists(path):
file.open(path, File.READ)
var is_first_line = true
var line: Array
while !file.eof_reached():
line = file.get_csv_line()
if is_first_line:
is_first_line = false
for i in range(2, line.size()):
commas.append("")
existing_csv[line[0]] = line
file.close()
# Start a new file
file.open(path, File.WRITE)
if not file.file_exists(path):
file.store_csv_line(["keys", "en"])
# Write our translations to file
var known_keys: PoolStringArray = []
var dialogue = parser.parse(editor.text).get("lines")
# Make a list of stuff that needs to go into the file
var lines_to_save = []
for key in dialogue.keys():
var line: Dictionary = dialogue.get(key)
if not line.get("type") in [DialogueConstants.TYPE_DIALOGUE, DialogueConstants.TYPE_RESPONSE]: continue
if line.get("text") in known_keys: continue
known_keys.append(line.get("text"))
if existing_csv.has(line.get("translation_key")):
var existing_line = existing_csv.get(line.get("translation_key"))
existing_line[1] = line.get("text")
lines_to_save.append(existing_line)
existing_csv.erase(line.get("translation_key"))
else:
known_keys.append(line.get("text"))
lines_to_save.append(PoolStringArray([line.get("translation_key"), line.get("text")] + commas))
# Store lines in the file, starting with anything that already exists that hasn't been touched
for line in existing_csv.values():
file.store_csv_line(line)
for line in lines_to_save:
file.store_csv_line(line)
file.close()
plugin.get_editor_interface().get_resource_filesystem().scan()
plugin.get_editor_interface().get_file_system_dock().navigate_to_path(path)
### Signals
func _on_open_menu_index_pressed(index):
var item = open_button.get_popup().get_item_text(index)
match item:
"Open...":
open_dialogue_dialog.popup_centered()
"Clear recent files":
recent_resources.clear()
settings.set_editor_value("recent_resources", recent_resources)
build_open_menu()
_:
open_resource_from_path(item)
func _on_insert_menu_id_pressed(id):
match id:
0:
editor.insert_bbcode("[wave amp=25 freq=5]", "[/wave]")
1:
editor.insert_bbcode("[shake rate=20 level=10]", "[/shake]")
3:
editor.insert_bbcode("[wait=1]")
4:
editor.insert_bbcode("[speed=0.2]")
5:
editor.insert_bbcode("[next=auto]")
func _on_translation_menu_id_pressed(id):
match id:
0:
generate_translations_keys()
1:
var filename = current_resource.resource_path.get_file().replace(".tres", ".csv")
var path = settings.get_editor_value("last_csv_path", current_resource.resource_path.get_base_dir()) + "/" + filename
save_translations_dialog.current_path = path
save_translations_dialog.popup_centered()
func _on_CodeEditor_text_changed():
has_changed = true
title_list.titles = editor.get_titles()
parse_timeout.start(1)
func _on_NewButton_pressed():
new_dialogue_dialog.popup_centered()
func _on_NewDialogueDialog_file_selected(path):
var resource = DialogueResource.new()
resource.take_over_path(path)
ResourceSaver.save(path, resource)
open_resource(resource)
func _on_FileLabel_pressed():
var file_system = plugin.get_editor_interface().get_file_system_dock()
file_system.navigate_to_path(current_resource.resource_path)
func _on_SettingsButton_pressed():
settings_dialog.popup_centered()
func _on_CodeEditor_active_title_changed(title):
title_list.select_title(title)
settings.set_editor_value("run_title", title)
run_node_button.hint_tooltip = "Play the test scene using \"%s\"" % title
func _on_ParseTimeout_timeout():
parse_timeout.stop()
parse()
func _on_TitleList_title_clicked(title):
editor.go_to_title(title)
func _on_OpenDialogueDialog_file_selected(path):
open_resource_from_path(path)
func _on_OpenDialogueDialog_confirmed():
open_resource_from_path(open_dialogue_dialog.current_path)
func _on_SettingsDialog_popup_hide():
parse(true)
editor.wrap_enabled = settings.get_editor_value("wrap_lines", false)
editor.grab_focus()
func _on_ErrorList_error_pressed(error):
editor.cursor_set_line(error.get("line"))
func _on_HelpButton_pressed():
OS.shell_open("https://github.com/nathanhoad/godot_dialogue_manager")
func _on_SaveTranslationsDialog_file_selected(path):
settings.set_editor_value("last_csv_path", path.get_base_dir())
save_translations(path)
func _on_UpdateChecker_has_update(version, url):
update_button.visible = true
update_button.text = "v" + version + " available!"
func _on_UpdateButton_pressed():
OS.shell_open(update_checker.plugin_url)
func _on_ErrorButton_pressed():
parse(true)
func _on_SettingsDialog_script_button_pressed(path):
plugin.get_editor_interface().edit_resource(load(path))
func _on_RunButton_pressed():
if settings.has_editor_value("run_title"):
settings.set_editor_value("run_resource", current_resource.resource_path)
plugin.get_editor_interface().play_custom_scene("res://addons/dialogue_manager/views/test_scene.tscn")
func _on_SearchButton_toggled(button_pressed):
if editor.last_selection_text:
search_toolbar.input.text = editor.last_selection_text
search_toolbar.visible = button_pressed
func _on_SearchToolbar_close_requested():
search_button.pressed = false
search_toolbar.visible = false
editor.grab_focus()
func _on_SearchToolbar_open_requested():
search_button.pressed = true
search_toolbar.visible = true