Some changes
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
uid://djeybvlb332mp
|
||||
@@ -0,0 +1,56 @@
|
||||
[gd_scene format=3 uid="uid://civ6shmka5e8u"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://klpiq4tk3t7a" path="res://addons/dialogue_manager/components/code_edit_syntax_highlighter.gd" id="1_58cfo"]
|
||||
[ext_resource type="Script" uid="uid://djeybvlb332mp" path="res://addons/dialogue_manager/components/code_edit.gd" id="1_g324i"]
|
||||
|
||||
[sub_resource type="SyntaxHighlighter" id="SyntaxHighlighter_crbvo"]
|
||||
script = ExtResource("1_58cfo")
|
||||
|
||||
[node name="CodeEdit" type="CodeEdit" unique_id=236286673]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
text = "~ title_thing
|
||||
|
||||
if this = \"that\" or 'this'
|
||||
Nathan: Something
|
||||
- Then [if test.thing() == 2.0] => somewhere
|
||||
- Other => END!
|
||||
|
||||
~ somewhere
|
||||
|
||||
set has_something = true
|
||||
=> END"
|
||||
scroll_past_end_of_file = true
|
||||
minimap_draw = true
|
||||
syntax_highlighter = SubResource("SyntaxHighlighter_crbvo")
|
||||
highlight_all_occurrences = true
|
||||
highlight_current_line = true
|
||||
draw_tabs = true
|
||||
symbol_lookup_on_click = true
|
||||
line_folding = true
|
||||
gutters_draw_line_numbers = true
|
||||
gutters_draw_fold_gutter = true
|
||||
delimiter_strings = Array[String](["\" \""])
|
||||
delimiter_comments = Array[String](["#"])
|
||||
code_completion_enabled = true
|
||||
code_completion_prefixes = Array[String]([">", "<"])
|
||||
indent_automatic = true
|
||||
auto_brace_completion_enabled = true
|
||||
auto_brace_completion_highlight_matching = true
|
||||
auto_brace_completion_pairs = {
|
||||
"\"": "\"",
|
||||
"(": ")",
|
||||
"[": "]",
|
||||
"{": "}"
|
||||
}
|
||||
script = ExtResource("1_g324i")
|
||||
|
||||
[connection signal="caret_changed" from="." to="." method="_on_code_edit_caret_changed"]
|
||||
[connection signal="gutter_clicked" from="." to="." method="_on_code_edit_gutter_clicked"]
|
||||
[connection signal="symbol_lookup" from="." to="." method="_on_code_edit_symbol_lookup"]
|
||||
[connection signal="symbol_validate" from="." to="." method="_on_code_edit_symbol_validate"]
|
||||
[connection signal="text_changed" from="." to="." method="_on_code_edit_text_changed"]
|
||||
[connection signal="text_set" from="." to="." method="_on_code_edit_text_set"]
|
||||
@@ -0,0 +1,237 @@
|
||||
@tool
|
||||
class_name DMSyntaxHighlighter extends SyntaxHighlighter
|
||||
|
||||
|
||||
var regex: DMCompilerRegEx = DMCompilerRegEx.new()
|
||||
var compilation: DMCompilation = DMCompilation.new()
|
||||
var expression_parser = DMExpressionParser.new()
|
||||
|
||||
var cache: Dictionary = {}
|
||||
|
||||
|
||||
func _clear_highlighting_cache() -> void:
|
||||
cache.clear()
|
||||
|
||||
|
||||
func _get_line_syntax_highlighting(line: int) -> Dictionary:
|
||||
expression_parser.include_comments = true
|
||||
|
||||
var colors: Dictionary = {}
|
||||
var text_edit: TextEdit = get_text_edit()
|
||||
var text: String = text_edit.get_line(line)
|
||||
|
||||
# Prevent an error from popping up while developing
|
||||
if not is_instance_valid(text_edit) or text_edit.theme_overrides.is_empty():
|
||||
return colors
|
||||
|
||||
# Disable this, as well as the line at the bottom of this function to remove the cache.
|
||||
if text in cache:
|
||||
return cache[text]
|
||||
|
||||
var theme: Dictionary = text_edit.theme_overrides
|
||||
|
||||
var index: int = 0
|
||||
|
||||
match DMCompiler.get_line_type(text):
|
||||
DMConstants.TYPE_USING:
|
||||
colors[index] = { color = theme.conditions_color }
|
||||
colors[index + "using ".length()] = { color = theme.text_color }
|
||||
|
||||
DMConstants.TYPE_IMPORT:
|
||||
colors[index] = { color = theme.conditions_color }
|
||||
var import: RegExMatch = regex.IMPORT_REGEX.search(text)
|
||||
if import:
|
||||
colors[index + import.get_start("path") - 1] = { color = theme.strings_color }
|
||||
colors[index + import.get_end("path") + 1] = { color = theme.conditions_color }
|
||||
colors[index + import.get_start("prefix")] = { color = theme.text_color }
|
||||
|
||||
DMConstants.TYPE_COMMENT:
|
||||
colors[index] = { color = theme.comments_color }
|
||||
|
||||
DMConstants.TYPE_TITLE:
|
||||
colors[index] = { color = theme.titles_color }
|
||||
|
||||
DMConstants.TYPE_CONDITION, DMConstants.TYPE_WHILE, DMConstants.TYPE_MATCH, DMConstants.TYPE_WHEN:
|
||||
colors[0] = { color = theme.conditions_color }
|
||||
index = text.find(" ")
|
||||
if index > -1:
|
||||
var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_CONDITION, 0)
|
||||
if expression.size() == 0:
|
||||
colors[index] = { color = theme.critical_color }
|
||||
else:
|
||||
_highlight_expression(expression, colors, index)
|
||||
|
||||
DMConstants.TYPE_MUTATION:
|
||||
colors[0] = { color = theme.mutations_line_color }
|
||||
index = text.find(" ")
|
||||
var expression: Array = expression_parser.tokenise(text.substr(index), DMConstants.TYPE_MUTATION, 0)
|
||||
if expression.size() == 0:
|
||||
colors[index] = { color = theme.critical_color }
|
||||
else:
|
||||
_highlight_expression(expression, colors, index)
|
||||
|
||||
DMConstants.TYPE_GOTO:
|
||||
if text.strip_edges().begins_with("%"):
|
||||
colors[index] = { color = theme.symbols_color }
|
||||
index = text.find(" ")
|
||||
_highlight_goto(text, colors, index)
|
||||
|
||||
DMConstants.TYPE_RANDOM:
|
||||
colors[index] = { color = theme.symbols_color }
|
||||
|
||||
DMConstants.TYPE_DIALOGUE, DMConstants.TYPE_RESPONSE:
|
||||
if text.strip_edges().begins_with("%"):
|
||||
colors[index] = { color = theme.symbols_color }
|
||||
index = text.find(" ", text.find("%"))
|
||||
colors[index] = { color = theme.text_color.lerp(theme.symbols_color, 0.5) }
|
||||
|
||||
var dialogue_text: String = text.substr(index, text.find("=>"))
|
||||
|
||||
# Highlight character name (but ignore ":" within line ID reference)
|
||||
var split_index: int = dialogue_text.replace("\\:", "??").find(":")
|
||||
if text.substr(split_index - 3, 3) != "[ID":
|
||||
colors[index + split_index + 1] = { color = theme.text_color }
|
||||
else:
|
||||
# If there's no character name then just highlight the text as dialogue.
|
||||
colors[index] = { color = theme.text_color }
|
||||
|
||||
# Interpolation
|
||||
var replacements: Array[RegExMatch] = regex.REPLACEMENTS_REGEX.search_all(dialogue_text)
|
||||
for replacement: RegExMatch in replacements:
|
||||
var expression_text: String = replacement.get_string().substr(0, replacement.get_string().length() - 2).substr(2)
|
||||
var expression: Array = expression_parser.tokenise(expression_text, DMConstants.TYPE_MUTATION, replacement.get_start())
|
||||
var expression_index: int = index + replacement.get_start()
|
||||
colors[expression_index] = { color = theme.symbols_color }
|
||||
if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR:
|
||||
colors[expression_index] = { color = theme.critical_color }
|
||||
else:
|
||||
_highlight_expression(expression, colors, index + 2)
|
||||
colors[expression_index + expression_text.length() + 2] = { color = theme.symbols_color }
|
||||
colors[expression_index + expression_text.length() + 4] = { color = theme.text_color }
|
||||
# Tags (and inline mutations)
|
||||
var resolved_line_data: DMResolvedLineData = DMResolvedLineData.new("")
|
||||
var bbcodes: Array[Dictionary] = resolved_line_data.find_bbcode_positions_in_string(dialogue_text, true, true)
|
||||
for bbcode: Dictionary in bbcodes:
|
||||
var tag: String = bbcode.code
|
||||
var code: String = bbcode.raw_args
|
||||
if code.begins_with("["):
|
||||
colors[index + bbcode.start] = { color = theme.symbols_color }
|
||||
colors[index + bbcode.start + 2] = { color = theme.text_color }
|
||||
var pipe_cursor: int = code.find("|")
|
||||
while pipe_cursor > -1:
|
||||
colors[index + bbcode.start + pipe_cursor + 1] = { color = theme.symbols_color }
|
||||
colors[index + bbcode.start + pipe_cursor + 2] = { color = theme.text_color }
|
||||
pipe_cursor = code.find("|", pipe_cursor + 1)
|
||||
colors[index + bbcode.end - 1] = { color = theme.symbols_color }
|
||||
colors[index + bbcode.end + 1] = { color = theme.text_color }
|
||||
else:
|
||||
colors[index + bbcode.start] = { color = theme.symbols_color }
|
||||
if tag.begins_with("$>") or tag.begins_with("do") or tag.begins_with("set") or tag.begins_with("if"):
|
||||
if tag.begins_with("if"):
|
||||
colors[index + bbcode.start + 1] = { color = theme.conditions_color }
|
||||
else:
|
||||
colors[index + bbcode.start + 1] = { color = theme.mutations_line_color }
|
||||
var expression: Array = expression_parser.tokenise(code, DMConstants.TYPE_MUTATION, bbcode.start + bbcode.code.length())
|
||||
if expression.size() == 0 or expression[0].type == DMConstants.TYPE_ERROR:
|
||||
colors[index + bbcode.start + tag.length() + 1] = { color = theme.critical_color }
|
||||
else:
|
||||
_highlight_expression(expression, colors, index + 2)
|
||||
# else and closing if have no expression
|
||||
elif tag.begins_with("else") or tag.begins_with("/if"):
|
||||
colors[index + bbcode.start + 1] = { color = theme.conditions_color }
|
||||
colors[index + bbcode.end] = { color = theme.symbols_color }
|
||||
colors[index + bbcode.end + 1] = { color = theme.text_color }
|
||||
# Jumps
|
||||
if "=> " in text or "=>< " in text:
|
||||
_highlight_goto(text, colors, index)
|
||||
|
||||
# Order the dictionary keys to prevent CodeEdit from having issues
|
||||
var ordered_colors: Dictionary = {}
|
||||
var ordered_keys: Array = colors.keys()
|
||||
ordered_keys.sort()
|
||||
for key_index: int in ordered_keys:
|
||||
ordered_colors[key_index] = colors[key_index]
|
||||
|
||||
cache[text] = ordered_colors
|
||||
return ordered_colors
|
||||
|
||||
|
||||
func _highlight_expression(tokens: Array, colors: Dictionary, index: int) -> int:
|
||||
var theme: Dictionary = get_text_edit().theme_overrides
|
||||
var last_index: int = index
|
||||
for token: Dictionary in tokens:
|
||||
last_index = token.i
|
||||
match token.type:
|
||||
DMConstants.TOKEN_COMMENT:
|
||||
colors[index + token.i] = { color = theme.comments_color }
|
||||
|
||||
DMConstants.TOKEN_CONDITION, DMConstants.TOKEN_AND_OR, DMConstants.TOKEN_NOT:
|
||||
colors[index + token.i] = { color = theme.conditions_color }
|
||||
|
||||
DMConstants.TOKEN_VARIABLE:
|
||||
if token.value in ["true", "false"]:
|
||||
colors[index + token.i] = { color = theme.conditions_color }
|
||||
else:
|
||||
colors[index + token.i] = { color = theme.members_color }
|
||||
|
||||
DMConstants.TOKEN_OPERATOR, DMConstants.TOKEN_COLON, \
|
||||
DMConstants.TOKEN_COMMA, DMConstants.TOKEN_DOT, DMConstants.TOKEN_NULL_COALESCE, \
|
||||
DMConstants.TOKEN_ASSIGNMENT, DMConstants.TOKEN_COMPARISON:
|
||||
if token.get("value", null) == "in":
|
||||
colors[index + token.i] = { color = theme.conditions_color }
|
||||
else:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
|
||||
DMConstants.TOKEN_NUMBER:
|
||||
colors[index + token.i] = { color = theme.numbers_color }
|
||||
|
||||
DMConstants.TOKEN_STRING:
|
||||
colors[index + token.i] = { color = theme.strings_color }
|
||||
|
||||
DMConstants.TOKEN_FUNCTION:
|
||||
colors[index + token.i] = { color = theme.mutations_color }
|
||||
colors[index + token.i + token.function.length()] = { color = theme.symbols_color }
|
||||
for parameter: Array in token.value:
|
||||
last_index = _highlight_expression(parameter, colors, index)
|
||||
DMConstants.TOKEN_PARENS_CLOSE:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
|
||||
DMConstants.TOKEN_DICTIONARY_REFERENCE:
|
||||
colors[index + token.i] = { color = theme.members_color }
|
||||
colors[index + token.i + token.variable.length()] = { color = theme.symbols_color }
|
||||
last_index = _highlight_expression(token.value, colors, index)
|
||||
DMConstants.TOKEN_ARRAY:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
for item: Array in token.value:
|
||||
last_index = _highlight_expression(item, colors, index)
|
||||
DMConstants.TOKEN_BRACKET_CLOSE:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
|
||||
DMConstants.TOKEN_DICTIONARY:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
last_index = _highlight_expression(token.value.keys() + token.value.values(), colors, index)
|
||||
DMConstants.TOKEN_BRACE_CLOSE:
|
||||
colors[index + token.i] = { color = theme.symbols_color }
|
||||
last_index += 1
|
||||
|
||||
DMConstants.TOKEN_GROUP:
|
||||
last_index = _highlight_expression(token.value, colors, index)
|
||||
|
||||
return last_index
|
||||
|
||||
|
||||
func _highlight_goto(text: String, colors: Dictionary, index: int) -> int:
|
||||
var theme: Dictionary = get_text_edit().theme_overrides
|
||||
var goto_data: DMResolvedGotoData = DMResolvedGotoData.new(text, {})
|
||||
colors[goto_data.index] = { color = theme.jumps_color }
|
||||
if "{{" in text:
|
||||
index = text.find("{{", goto_data.index)
|
||||
var last_index: int = 0
|
||||
if goto_data.error:
|
||||
colors[index + 2] = { color = theme.critical_color }
|
||||
else:
|
||||
last_index = _highlight_expression(goto_data.expression, colors, index)
|
||||
index = text.find("}}", index + last_index)
|
||||
colors[index] = { color = theme.jumps_color }
|
||||
|
||||
return index
|
||||
@@ -0,0 +1 @@
|
||||
uid://klpiq4tk3t7a
|
||||
@@ -0,0 +1,84 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
signal failed()
|
||||
signal updated(updated_to_version: String)
|
||||
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
|
||||
const TEMP_FILE_NAME = "user://temp.zip"
|
||||
|
||||
|
||||
@onready var logo: TextureRect = %Logo
|
||||
@onready var label: Label = $VBox/Label
|
||||
@onready var http_request: HTTPRequest = $HTTPRequest
|
||||
@onready var download_button: Button = %DownloadButton
|
||||
|
||||
var next_version_release: Dictionary:
|
||||
set(value):
|
||||
next_version_release = value
|
||||
label.text = DialogueConstants.translate(&"update.is_available_for_download") % value.tag_name.substr(1)
|
||||
get:
|
||||
return next_version_release
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$VBox/Center/DownloadButton.text = DialogueConstants.translate(&"update.download_update")
|
||||
$VBox/Center2/NotesButton.text = DialogueConstants.translate(&"update.release_notes")
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_download_button_pressed() -> void:
|
||||
# Safeguard the actual dialogue manager repo from accidentally updating itself
|
||||
if FileAccess.file_exists("res://tests/test_basic_dialogue.gd"):
|
||||
prints("You can't update the addon from within itself.")
|
||||
failed.emit()
|
||||
return
|
||||
|
||||
http_request.request(next_version_release.zipball_url)
|
||||
download_button.disabled = true
|
||||
download_button.text = DialogueConstants.translate(&"update.downloading")
|
||||
|
||||
|
||||
func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
failed.emit()
|
||||
return
|
||||
|
||||
# Save the downloaded zip
|
||||
var zip_file: FileAccess = FileAccess.open(TEMP_FILE_NAME, FileAccess.WRITE)
|
||||
zip_file.store_buffer(body)
|
||||
zip_file.close()
|
||||
|
||||
OS.move_to_trash(ProjectSettings.globalize_path("res://addons/dialogue_manager"))
|
||||
|
||||
var zip_reader: ZIPReader = ZIPReader.new()
|
||||
zip_reader.open(TEMP_FILE_NAME)
|
||||
var files: PackedStringArray = zip_reader.get_files()
|
||||
|
||||
var base_path = files[1]
|
||||
# Remove archive folder
|
||||
files.remove_at(0)
|
||||
# Remove assets folder
|
||||
files.remove_at(0)
|
||||
|
||||
for path in files:
|
||||
var new_file_path: String = path.replace(base_path, "")
|
||||
if path.ends_with("/"):
|
||||
DirAccess.make_dir_recursive_absolute("res://addons/%s" % new_file_path)
|
||||
else:
|
||||
var file: FileAccess = FileAccess.open("res://addons/%s" % new_file_path, FileAccess.WRITE)
|
||||
file.store_buffer(zip_reader.read_file(path))
|
||||
|
||||
zip_reader.close()
|
||||
DirAccess.remove_absolute(TEMP_FILE_NAME)
|
||||
|
||||
updated.emit(next_version_release.tag_name.substr(1))
|
||||
|
||||
|
||||
func _on_notes_button_pressed() -> void:
|
||||
OS.shell_open(next_version_release.html_url)
|
||||
@@ -0,0 +1 @@
|
||||
uid://kpwo418lb2t2
|
||||
@@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://qdxrxv3c3hxk"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://kpwo418lb2t2" path="res://addons/dialogue_manager/components/download_update_panel.gd" id="1_4tm1k"]
|
||||
[ext_resource type="Texture2D" uid="uid://d3baj6rygkb3f" path="res://addons/dialogue_manager/assets/update.svg" id="2_4o2m6"]
|
||||
|
||||
[node name="DownloadUpdatePanel" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_4tm1k")
|
||||
|
||||
[node name="HTTPRequest" type="HTTPRequest" parent="."]
|
||||
|
||||
[node name="VBox" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -1.0
|
||||
offset_top = 9.0
|
||||
offset_right = -1.0
|
||||
offset_bottom = 9.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="Logo" type="TextureRect" parent="VBox"]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(300, 80)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("2_4o2m6")
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="Label" type="Label" parent="VBox"]
|
||||
layout_mode = 2
|
||||
text = "v1.2.3 is available for download."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="Center" type="CenterContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DownloadButton" type="Button" parent="VBox/Center"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Download update"
|
||||
|
||||
[node name="Center2" type="CenterContainer" parent="VBox"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="NotesButton" type="LinkButton" parent="VBox/Center2"]
|
||||
layout_mode = 2
|
||||
text = "Read release notes"
|
||||
|
||||
[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"]
|
||||
[connection signal="pressed" from="VBox/Center/DownloadButton" to="." method="_on_download_button_pressed"]
|
||||
[connection signal="pressed" from="VBox/Center2/NotesButton" to="." method="_on_notes_button_pressed"]
|
||||
@@ -0,0 +1,45 @@
|
||||
@tool
|
||||
|
||||
class_name DMDialogueEditorProperty extends EditorProperty
|
||||
|
||||
|
||||
const DialoguePropertyEditorControl: PackedScene = preload("./editor_property_control.tscn")
|
||||
|
||||
|
||||
var control = DialoguePropertyEditorControl.instantiate()
|
||||
var current_value: DialogueResource
|
||||
var is_updating: bool = false
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
add_child(control)
|
||||
|
||||
control.resource = current_value
|
||||
|
||||
control.resource_changed.connect(_on_resource_changed)
|
||||
|
||||
|
||||
func _update_property() -> void:
|
||||
var next_value: DialogueResource = get_edited_object()[get_edited_property()]
|
||||
|
||||
# The resource might have been deleted elsewhere so check that it's not in a weird state
|
||||
if is_instance_valid(next_value) and not next_value.resource_path.ends_with(".dialogue"):
|
||||
emit_changed(get_edited_property(), null)
|
||||
return
|
||||
|
||||
if next_value == current_value: return
|
||||
|
||||
is_updating = true
|
||||
current_value = next_value
|
||||
control.resource = current_value
|
||||
is_updating = false
|
||||
|
||||
|
||||
#region Signals
|
||||
|
||||
|
||||
func _on_resource_changed(next_resource: DialogueResource) -> void:
|
||||
emit_changed(get_edited_property(), next_resource)
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://nyypeje1a036
|
||||
@@ -0,0 +1,153 @@
|
||||
@tool
|
||||
|
||||
extends HBoxContainer
|
||||
|
||||
|
||||
signal pressed()
|
||||
signal resource_changed(next_resource: DialogueResource)
|
||||
|
||||
|
||||
const ITEM_NEW: int = 100
|
||||
const ITEM_QUICK_LOAD: int = 200
|
||||
const ITEM_LOAD: int = 201
|
||||
const ITEM_EDIT: int = 300
|
||||
const ITEM_CLEAR: int = 301
|
||||
const ITEM_FILESYSTEM: int = 400
|
||||
|
||||
|
||||
@onready var button: Button = $ResourceButton
|
||||
@onready var menu_button: Button = $MenuButton
|
||||
@onready var menu: PopupMenu = $Menu
|
||||
@onready var quick_open_dialog: ConfirmationDialog = $QuickOpenDialog
|
||||
@onready var files_list = $QuickOpenDialog/FilesList
|
||||
@onready var new_dialog: FileDialog = $NewDialog
|
||||
@onready var open_dialog: FileDialog = $OpenDialog
|
||||
|
||||
var resource: Resource:
|
||||
set(next_resource):
|
||||
resource = next_resource
|
||||
if button:
|
||||
button.resource = resource
|
||||
get:
|
||||
return resource
|
||||
|
||||
var is_waiting_for_file: bool = false
|
||||
var quick_selected_file: String = ""
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
menu_button.icon = get_theme_icon("GuiDropdown", "EditorIcons")
|
||||
|
||||
|
||||
func build_menu() -> void:
|
||||
menu.clear()
|
||||
|
||||
menu.add_icon_item(DMPlugin.instance._get_plugin_icon(), "New Dialogue", ITEM_NEW)
|
||||
menu.add_separator()
|
||||
menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), "Quick Load", ITEM_QUICK_LOAD)
|
||||
menu.add_icon_item(get_theme_icon("Load", "EditorIcons"), "Load", ITEM_LOAD)
|
||||
if resource:
|
||||
menu.add_icon_item(get_theme_icon("Edit", "EditorIcons"), "Edit", ITEM_EDIT)
|
||||
menu.add_icon_item(get_theme_icon("Clear", "EditorIcons"), "Clear", ITEM_CLEAR)
|
||||
menu.add_separator()
|
||||
menu.add_item("Show in FileSystem", ITEM_FILESYSTEM)
|
||||
|
||||
menu.size = Vector2.ZERO
|
||||
|
||||
|
||||
#region Signals
|
||||
|
||||
|
||||
func _on_new_dialog_file_selected(path: String) -> void:
|
||||
DMPlugin.instance.main_view.new_file(path)
|
||||
is_waiting_for_file = false
|
||||
if DMCache.has_file(path):
|
||||
resource_changed.emit(load(path))
|
||||
else:
|
||||
var next_resource: DialogueResource = await DMPlugin.instance.import_plugin.compiled_resource
|
||||
next_resource.resource_path = path
|
||||
resource_changed.emit(next_resource)
|
||||
|
||||
|
||||
func _on_open_dialog_file_selected(file: String) -> void:
|
||||
resource_changed.emit(load(file))
|
||||
|
||||
|
||||
func _on_file_dialog_canceled() -> void:
|
||||
is_waiting_for_file = false
|
||||
|
||||
|
||||
func _on_resource_button_pressed() -> void:
|
||||
if is_instance_valid(resource):
|
||||
EditorInterface.call_deferred("edit_resource", resource)
|
||||
|
||||
elif menu.visible:
|
||||
menu.hide()
|
||||
else:
|
||||
build_menu()
|
||||
menu.position = get_viewport().position + Vector2i(
|
||||
button.global_position.x + button.size.x - menu.size.x,
|
||||
2 + menu_button.global_position.y + button.size.y
|
||||
)
|
||||
menu.popup()
|
||||
|
||||
|
||||
func _on_resource_button_resource_dropped(next_resource: Resource) -> void:
|
||||
resource_changed.emit(next_resource)
|
||||
|
||||
|
||||
func _on_menu_button_pressed() -> void:
|
||||
if menu.visible:
|
||||
menu.hide()
|
||||
else:
|
||||
build_menu()
|
||||
menu.position = get_viewport().position + Vector2i(
|
||||
menu_button.global_position.x + menu_button.size.x - menu.size.x,
|
||||
2 + menu_button.global_position.y + menu_button.size.y
|
||||
)
|
||||
menu.popup()
|
||||
|
||||
|
||||
func _on_menu_id_pressed(id: int) -> void:
|
||||
match id:
|
||||
ITEM_NEW:
|
||||
is_waiting_for_file = true
|
||||
new_dialog.popup_centered()
|
||||
|
||||
ITEM_QUICK_LOAD:
|
||||
quick_selected_file = ""
|
||||
files_list.files = DMCache.get_files()
|
||||
if resource:
|
||||
files_list.select_file(resource.resource_path)
|
||||
quick_open_dialog.popup_centered()
|
||||
files_list.focus_filter()
|
||||
|
||||
ITEM_LOAD:
|
||||
is_waiting_for_file = true
|
||||
open_dialog.popup_centered()
|
||||
|
||||
ITEM_EDIT:
|
||||
EditorInterface.call_deferred("edit_resource", resource)
|
||||
|
||||
ITEM_CLEAR:
|
||||
resource_changed.emit(null)
|
||||
|
||||
ITEM_FILESYSTEM:
|
||||
EditorInterface.get_file_system_dock().navigate_to_path(resource.resource_path)
|
||||
|
||||
|
||||
func _on_files_list_file_double_clicked(file_path: String) -> void:
|
||||
resource_changed.emit(load(file_path))
|
||||
quick_open_dialog.hide()
|
||||
|
||||
|
||||
func _on_files_list_file_selected(file_path: String) -> void:
|
||||
quick_selected_file = file_path
|
||||
|
||||
|
||||
func _on_quick_open_dialog_confirmed() -> void:
|
||||
if quick_selected_file != "":
|
||||
resource_changed.emit(load(quick_selected_file))
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://dooe2pflnqtve
|
||||
@@ -0,0 +1,58 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://ycn6uaj7dsrh"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dooe2pflnqtve" path="res://addons/dialogue_manager/components/editor_property/editor_property_control.gd" id="1_het12"]
|
||||
[ext_resource type="PackedScene" uid="uid://b16uuqjuof3n5" path="res://addons/dialogue_manager/components/editor_property/resource_button.tscn" id="2_hh3d4"]
|
||||
[ext_resource type="PackedScene" uid="uid://dnufpcdrreva3" path="res://addons/dialogue_manager/components/files_list.tscn" id="3_l8fp6"]
|
||||
|
||||
[node name="PropertyEditorButton" type="HBoxContainer"]
|
||||
offset_right = 40.0
|
||||
offset_bottom = 40.0
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 0
|
||||
script = ExtResource("1_het12")
|
||||
|
||||
[node name="ResourceButton" parent="." instance=ExtResource("2_hh3d4")]
|
||||
layout_mode = 2
|
||||
text = "<empty>"
|
||||
text_overrun_behavior = 3
|
||||
clip_text = true
|
||||
|
||||
[node name="MenuButton" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Menu" type="PopupMenu" parent="."]
|
||||
|
||||
[node name="QuickOpenDialog" type="ConfirmationDialog" parent="."]
|
||||
title = "Find Dialogue Resource"
|
||||
size = Vector2i(400, 600)
|
||||
min_size = Vector2i(400, 600)
|
||||
ok_button_text = "Open"
|
||||
|
||||
[node name="FilesList" parent="QuickOpenDialog" instance=ExtResource("3_l8fp6")]
|
||||
|
||||
[node name="NewDialog" type="FileDialog" parent="."]
|
||||
size = Vector2i(900, 750)
|
||||
min_size = Vector2i(900, 750)
|
||||
dialog_hide_on_ok = true
|
||||
filters = PackedStringArray("*.dialogue ; Dialogue")
|
||||
|
||||
[node name="OpenDialog" type="FileDialog" parent="."]
|
||||
title = "Open a File"
|
||||
size = Vector2i(900, 750)
|
||||
min_size = Vector2i(900, 750)
|
||||
ok_button_text = "Open"
|
||||
dialog_hide_on_ok = true
|
||||
file_mode = 0
|
||||
filters = PackedStringArray("*.dialogue ; Dialogue")
|
||||
|
||||
[connection signal="pressed" from="ResourceButton" to="." method="_on_resource_button_pressed"]
|
||||
[connection signal="resource_dropped" from="ResourceButton" to="." method="_on_resource_button_resource_dropped"]
|
||||
[connection signal="pressed" from="MenuButton" to="." method="_on_menu_button_pressed"]
|
||||
[connection signal="id_pressed" from="Menu" to="." method="_on_menu_id_pressed"]
|
||||
[connection signal="confirmed" from="QuickOpenDialog" to="." method="_on_quick_open_dialog_confirmed"]
|
||||
[connection signal="file_double_clicked" from="QuickOpenDialog/FilesList" to="." method="_on_files_list_file_double_clicked"]
|
||||
[connection signal="file_selected" from="QuickOpenDialog/FilesList" to="." method="_on_files_list_file_selected"]
|
||||
[connection signal="canceled" from="NewDialog" to="." method="_on_file_dialog_canceled"]
|
||||
[connection signal="file_selected" from="NewDialog" to="." method="_on_new_dialog_file_selected"]
|
||||
[connection signal="canceled" from="OpenDialog" to="." method="_on_file_dialog_canceled"]
|
||||
[connection signal="file_selected" from="OpenDialog" to="." method="_on_open_dialog_file_selected"]
|
||||
@@ -0,0 +1,48 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
|
||||
signal resource_dropped(next_resource: Resource)
|
||||
|
||||
|
||||
var resource: Resource:
|
||||
set(next_resource):
|
||||
resource = next_resource
|
||||
if resource:
|
||||
icon = DMPlugin.instance._get_plugin_icon()
|
||||
text = resource.resource_path.get_file().replace(".dialogue", "")
|
||||
else:
|
||||
icon = null
|
||||
text = "<empty>"
|
||||
get:
|
||||
return resource
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match what:
|
||||
NOTIFICATION_DRAG_BEGIN:
|
||||
var data = get_viewport().gui_get_drag_data()
|
||||
if typeof(data) == TYPE_DICTIONARY and data.type == "files" and data.files.size() > 0 and data.files[0].ends_with(".dialogue"):
|
||||
add_theme_stylebox_override("normal", get_theme_stylebox("focus", "LineEdit"))
|
||||
add_theme_stylebox_override("hover", get_theme_stylebox("focus", "LineEdit"))
|
||||
|
||||
NOTIFICATION_DRAG_END:
|
||||
self.resource = resource
|
||||
remove_theme_stylebox_override("normal")
|
||||
remove_theme_stylebox_override("hover")
|
||||
|
||||
|
||||
func _can_drop_data(at_position: Vector2, data) -> bool:
|
||||
if typeof(data) != TYPE_DICTIONARY: return false
|
||||
if data.type != "files": return false
|
||||
|
||||
var files: PackedStringArray = Array(data.files).filter(func(f): return f.get_extension() == "dialogue")
|
||||
return files.size() > 0
|
||||
|
||||
|
||||
func _drop_data(at_position: Vector2, data) -> void:
|
||||
var files: PackedStringArray = Array(data.files).filter(func(f): return f.get_extension() == "dialogue")
|
||||
|
||||
if files.size() == 0: return
|
||||
|
||||
resource_dropped.emit(load(files[0]))
|
||||
@@ -0,0 +1 @@
|
||||
uid://damhqta55t67c
|
||||
@@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b16uuqjuof3n5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://damhqta55t67c" path="res://addons/dialogue_manager/components/editor_property/resource_button.gd" id="1_7u2i7"]
|
||||
|
||||
[node name="ResourceButton" type="Button"]
|
||||
offset_right = 8.0
|
||||
offset_bottom = 8.0
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_7u2i7")
|
||||
@@ -0,0 +1,85 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
|
||||
signal error_pressed(line_number)
|
||||
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
|
||||
|
||||
@onready var error_button: Button = $ErrorButton
|
||||
@onready var next_button: Button = $NextButton
|
||||
@onready var count_label: Label = $CountLabel
|
||||
@onready var previous_button: Button = $PreviousButton
|
||||
|
||||
## The index of the current error being shown
|
||||
var error_index: int = 0:
|
||||
set(next_error_index):
|
||||
error_index = wrap(next_error_index, 0, errors.size())
|
||||
show_error()
|
||||
get:
|
||||
return error_index
|
||||
|
||||
## The list of all errors
|
||||
var errors: Array = []:
|
||||
set(next_errors):
|
||||
errors = next_errors
|
||||
self.error_index = 0
|
||||
get:
|
||||
return errors
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
apply_theme()
|
||||
hide()
|
||||
|
||||
|
||||
## Set up colors and icons
|
||||
func apply_theme() -> void:
|
||||
error_button.add_theme_color_override("font_color", get_theme_color("error_color", "Editor"))
|
||||
error_button.add_theme_color_override("font_hover_color", get_theme_color("error_color", "Editor"))
|
||||
error_button.icon = get_theme_icon("StatusError", "EditorIcons")
|
||||
previous_button.icon = get_theme_icon("ArrowLeft", "EditorIcons")
|
||||
next_button.icon = get_theme_icon("ArrowRight", "EditorIcons")
|
||||
|
||||
|
||||
## Move the error index to match a given line
|
||||
func show_error_for_line_number(line_number: int) -> void:
|
||||
for i in range(0, errors.size()):
|
||||
if errors[i].line_number == line_number:
|
||||
self.error_index = i
|
||||
|
||||
|
||||
## Show the current error
|
||||
func show_error() -> void:
|
||||
if errors.size() == 0:
|
||||
hide()
|
||||
else:
|
||||
show()
|
||||
count_label.text = DialogueConstants.translate(&"n_of_n").format({ index = error_index + 1, total = errors.size() })
|
||||
var error = errors[error_index]
|
||||
error_button.text = DialogueConstants.translate(&"errors.line_and_message").format({ line = error.line_number, column = error.column_number, message = DialogueConstants.get_error_message(error.error) })
|
||||
if error.has("external_error"):
|
||||
error_button.text += " " + DialogueConstants.get_error_message(error.external_error)
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_errors_panel_theme_changed() -> void:
|
||||
apply_theme()
|
||||
|
||||
|
||||
func _on_error_button_pressed() -> void:
|
||||
error_pressed.emit(errors[error_index].line_number, errors[error_index].column_number)
|
||||
|
||||
|
||||
func _on_previous_button_pressed() -> void:
|
||||
self.error_index -= 1
|
||||
_on_error_button_pressed()
|
||||
|
||||
|
||||
func _on_next_button_pressed() -> void:
|
||||
self.error_index += 1
|
||||
_on_error_button_pressed()
|
||||
@@ -0,0 +1 @@
|
||||
uid://d2l8nlb6hhrfp
|
||||
@@ -0,0 +1,56 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://cs8pwrxr5vxix"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d2l8nlb6hhrfp" path="res://addons/dialogue_manager/components/errors_panel.gd" id="1_nfm3c"]
|
||||
|
||||
[sub_resource type="Image" id="Image_w0gko"]
|
||||
data = {
|
||||
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 16,
|
||||
"mipmaps": false,
|
||||
"width": 16
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_s6fxl"]
|
||||
image = SubResource("Image_w0gko")
|
||||
|
||||
[node name="ErrorsPanel" type="HBoxContainer"]
|
||||
visible = false
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 600.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_nfm3c")
|
||||
metadata/_edit_layout_mode = 1
|
||||
|
||||
[node name="ErrorButton" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/h_separation = 3
|
||||
icon = SubResource("ImageTexture_s6fxl")
|
||||
flat = true
|
||||
alignment = 0
|
||||
text_overrun_behavior = 4
|
||||
|
||||
[node name="Spacer" type="Control" parent="."]
|
||||
custom_minimum_size = Vector2(40, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PreviousButton" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_s6fxl")
|
||||
flat = true
|
||||
|
||||
[node name="CountLabel" type="Label" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="NextButton" type="Button" parent="."]
|
||||
layout_mode = 2
|
||||
icon = SubResource("ImageTexture_s6fxl")
|
||||
flat = true
|
||||
|
||||
[connection signal="pressed" from="ErrorButton" to="." method="_on_error_button_pressed"]
|
||||
[connection signal="pressed" from="PreviousButton" to="." method="_on_previous_button_pressed"]
|
||||
[connection signal="pressed" from="NextButton" to="." method="_on_next_button_pressed"]
|
||||
@@ -0,0 +1,150 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
signal file_selected(file_path: String)
|
||||
signal file_popup_menu_requested(at_position: Vector2)
|
||||
signal file_double_clicked(file_path: String)
|
||||
signal file_middle_clicked(file_path: String)
|
||||
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
|
||||
const MODIFIED_SUFFIX = "(*)"
|
||||
|
||||
|
||||
@export var icon: Texture2D
|
||||
|
||||
@onready var filter_edit: LineEdit = $FilterEdit
|
||||
@onready var list: ItemList = $List
|
||||
|
||||
var file_map: Dictionary = {}
|
||||
|
||||
var current_file_path: String = ""
|
||||
var last_selected_file_path: String = ""
|
||||
|
||||
var files: PackedStringArray = []:
|
||||
set(next_files):
|
||||
files = next_files
|
||||
files.sort()
|
||||
update_file_map()
|
||||
apply_filter()
|
||||
get:
|
||||
return files
|
||||
|
||||
var unsaved_files: Array[String] = []
|
||||
|
||||
var filter: String = "":
|
||||
set(next_filter):
|
||||
filter = next_filter
|
||||
apply_filter()
|
||||
get:
|
||||
return filter
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
apply_theme()
|
||||
|
||||
filter_edit.placeholder_text = DialogueConstants.translate(&"files_list.filter")
|
||||
|
||||
|
||||
func focus_filter() -> void:
|
||||
filter_edit.grab_focus()
|
||||
|
||||
|
||||
func select_file(file: String) -> void:
|
||||
list.deselect_all()
|
||||
for i in range(0, list.get_item_count()):
|
||||
var item_text = list.get_item_text(i).replace(MODIFIED_SUFFIX, "")
|
||||
if item_text == get_nice_file(file, item_text.count("/") + 1):
|
||||
list.select(i)
|
||||
last_selected_file_path = file
|
||||
|
||||
|
||||
func mark_file_as_unsaved(file: String, is_unsaved: bool) -> void:
|
||||
if not file in unsaved_files and is_unsaved:
|
||||
unsaved_files.append(file)
|
||||
elif file in unsaved_files and not is_unsaved:
|
||||
unsaved_files.erase(file)
|
||||
apply_filter()
|
||||
|
||||
|
||||
func update_file_map() -> void:
|
||||
file_map = {}
|
||||
for file in files:
|
||||
var nice_file: String = get_nice_file(file)
|
||||
|
||||
# See if a value with just the file name is already in the map
|
||||
for key in file_map.keys():
|
||||
if file_map[key] == nice_file:
|
||||
var bit_count = nice_file.count("/") + 2
|
||||
|
||||
var existing_nice_file = get_nice_file(key, bit_count)
|
||||
nice_file = get_nice_file(file, bit_count)
|
||||
|
||||
while nice_file == existing_nice_file:
|
||||
bit_count += 1
|
||||
existing_nice_file = get_nice_file(key, bit_count)
|
||||
nice_file = get_nice_file(file, bit_count)
|
||||
|
||||
file_map[key] = existing_nice_file
|
||||
|
||||
file_map[file] = nice_file
|
||||
|
||||
|
||||
func get_nice_file(file_path: String, path_bit_count: int = 1) -> String:
|
||||
var bits = file_path.replace("res://", "").replace(".dialogue", "").split("/")
|
||||
bits = bits.slice(-path_bit_count)
|
||||
return "/".join(bits)
|
||||
|
||||
|
||||
func apply_filter() -> void:
|
||||
list.clear()
|
||||
for file in file_map.keys():
|
||||
if filter == "" or filter.to_lower() in file.to_lower():
|
||||
var nice_file = file_map[file]
|
||||
if file in unsaved_files:
|
||||
nice_file += MODIFIED_SUFFIX
|
||||
var new_id := list.add_item(nice_file)
|
||||
list.set_item_icon(new_id, icon)
|
||||
|
||||
select_file(current_file_path)
|
||||
|
||||
|
||||
func apply_theme() -> void:
|
||||
if is_instance_valid(filter_edit):
|
||||
filter_edit.right_icon = get_theme_icon("Search", "EditorIcons")
|
||||
if is_instance_valid(list):
|
||||
list.add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Panel"))
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_theme_changed() -> void:
|
||||
apply_theme()
|
||||
|
||||
|
||||
func _on_filter_edit_text_changed(new_text: String) -> void:
|
||||
self.filter = new_text
|
||||
|
||||
|
||||
func _on_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void:
|
||||
var item_text = list.get_item_text(index).replace(MODIFIED_SUFFIX, "")
|
||||
var file = file_map.find_key(item_text)
|
||||
|
||||
if mouse_button_index == MOUSE_BUTTON_LEFT or mouse_button_index == MOUSE_BUTTON_RIGHT:
|
||||
select_file(file)
|
||||
file_selected.emit(file)
|
||||
if mouse_button_index == MOUSE_BUTTON_RIGHT:
|
||||
file_popup_menu_requested.emit(at_position)
|
||||
|
||||
if mouse_button_index == MOUSE_BUTTON_MIDDLE:
|
||||
file_middle_clicked.emit(file)
|
||||
|
||||
|
||||
func _on_list_item_activated(index: int) -> void:
|
||||
var item_text = list.get_item_text(index).replace(MODIFIED_SUFFIX, "")
|
||||
var file = file_map.find_key(item_text)
|
||||
select_file(file)
|
||||
file_double_clicked.emit(file)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dqa4a4wwoo0aa
|
||||
@@ -0,0 +1,29 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://dnufpcdrreva3"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dqa4a4wwoo0aa" path="res://addons/dialogue_manager/components/files_list.gd" id="1_cytii"]
|
||||
[ext_resource type="Texture2D" uid="uid://d3lr2uas6ax8v" path="res://addons/dialogue_manager/assets/icon.svg" id="2_3ijx1"]
|
||||
|
||||
[node name="FilesList" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_cytii")
|
||||
icon = ExtResource("2_3ijx1")
|
||||
|
||||
[node name="FilterEdit" type="LineEdit" parent="."]
|
||||
layout_mode = 2
|
||||
placeholder_text = "Filter files"
|
||||
clear_button_enabled = true
|
||||
|
||||
[node name="List" type="ItemList" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
allow_rmb_select = true
|
||||
|
||||
[connection signal="theme_changed" from="." to="." method="_on_theme_changed"]
|
||||
[connection signal="text_changed" from="FilterEdit" to="." method="_on_filter_edit_text_changed"]
|
||||
[connection signal="item_activated" from="List" to="." method="_on_list_item_activated"]
|
||||
[connection signal="item_clicked" from="List" to="." method="_on_list_item_clicked"]
|
||||
@@ -0,0 +1,222 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
signal open_requested()
|
||||
signal close_requested()
|
||||
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
|
||||
|
||||
@onready var input: LineEdit = $Search/Input
|
||||
@onready var result_label: Label = $Search/ResultLabel
|
||||
@onready var previous_button: Button = $Search/PreviousButton
|
||||
@onready var next_button: Button = $Search/NextButton
|
||||
@onready var match_case_button: CheckBox = $Search/MatchCaseCheckBox
|
||||
@onready var replace_check_button: CheckButton = $Search/ReplaceCheckButton
|
||||
@onready var replace_panel: HBoxContainer = $Replace
|
||||
@onready var replace_input: LineEdit = $Replace/Input
|
||||
@onready var replace_button: Button = $Replace/ReplaceButton
|
||||
@onready var replace_all_button: Button = $Replace/ReplaceAllButton
|
||||
|
||||
# The code edit we will be affecting (for some reason exporting this didn't work)
|
||||
var code_edit: CodeEdit:
|
||||
set(next_code_edit):
|
||||
code_edit = next_code_edit
|
||||
code_edit.gui_input.connect(_on_text_edit_gui_input)
|
||||
code_edit.text_changed.connect(_on_text_edit_text_changed)
|
||||
get:
|
||||
return code_edit
|
||||
|
||||
var results: Array = []
|
||||
var result_index: int = -1:
|
||||
set(next_result_index):
|
||||
result_index = next_result_index
|
||||
if results.size() > 0:
|
||||
var r = results[result_index]
|
||||
code_edit.set_caret_line(r[0])
|
||||
code_edit.select(r[0], r[1], r[0], r[1] + r[2])
|
||||
code_edit.center_viewport_to_caret()
|
||||
else:
|
||||
result_index = -1
|
||||
if is_instance_valid(code_edit):
|
||||
code_edit.deselect()
|
||||
|
||||
result_label.text = DialogueConstants.translate(&"n_of_n").format({ index = result_index + 1, total = results.size() })
|
||||
get:
|
||||
return result_index
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
apply_theme()
|
||||
|
||||
input.placeholder_text = DialogueConstants.translate(&"search.placeholder")
|
||||
previous_button.tooltip_text = DialogueConstants.translate(&"search.previous")
|
||||
next_button.tooltip_text = DialogueConstants.translate(&"search.next")
|
||||
match_case_button.text = DialogueConstants.translate(&"search.match_case")
|
||||
$Search/ReplaceCheckButton.text = DialogueConstants.translate(&"search.toggle_replace")
|
||||
replace_button.text = DialogueConstants.translate(&"search.replace")
|
||||
replace_all_button.text = DialogueConstants.translate(&"search.replace_all")
|
||||
$Replace/ReplaceLabel.text = DialogueConstants.translate(&"search.replace_with")
|
||||
|
||||
self.result_index = -1
|
||||
|
||||
replace_panel.hide()
|
||||
replace_button.disabled = true
|
||||
replace_all_button.disabled = true
|
||||
|
||||
hide()
|
||||
|
||||
|
||||
func focus_line_edit() -> void:
|
||||
input.grab_focus()
|
||||
input.select_all()
|
||||
search()
|
||||
|
||||
|
||||
func apply_theme() -> void:
|
||||
if is_instance_valid(previous_button):
|
||||
previous_button.icon = get_theme_icon("ArrowLeft", "EditorIcons")
|
||||
if is_instance_valid(next_button):
|
||||
next_button.icon = get_theme_icon("ArrowRight", "EditorIcons")
|
||||
|
||||
|
||||
# Find text in the code
|
||||
func search(text: String = "", default_result_index: int = 0) -> void:
|
||||
results.clear()
|
||||
|
||||
if text == "":
|
||||
text = input.text
|
||||
|
||||
var lines = code_edit.text.split("\n")
|
||||
for line_number in range(0, lines.size()):
|
||||
var line = lines[line_number]
|
||||
|
||||
var column = find_in_line(line, text, 0)
|
||||
while column > -1:
|
||||
results.append([line_number, column, text.length()])
|
||||
column = find_in_line(line, text, column + 1)
|
||||
|
||||
if results.size() > 0:
|
||||
replace_button.disabled = false
|
||||
replace_all_button.disabled = false
|
||||
else:
|
||||
replace_button.disabled = true
|
||||
replace_all_button.disabled = true
|
||||
|
||||
self.result_index = clamp(default_result_index, 0, results.size() - 1)
|
||||
|
||||
|
||||
# Find text in a string and match case if requested
|
||||
func find_in_line(line: String, text: String, from_index: int = 0) -> int:
|
||||
if match_case_button.button_pressed:
|
||||
return line.find(text, from_index)
|
||||
else:
|
||||
return line.findn(text, from_index)
|
||||
|
||||
|
||||
#region Signals
|
||||
|
||||
|
||||
func _on_text_edit_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.is_pressed():
|
||||
match event.as_text():
|
||||
"Ctrl+F", "Command+F":
|
||||
open_requested.emit()
|
||||
get_viewport().set_input_as_handled()
|
||||
"Ctrl+Shift+R", "Command+Shift+R":
|
||||
replace_check_button.set_pressed(true)
|
||||
open_requested.emit()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_text_edit_text_changed() -> void:
|
||||
results.clear()
|
||||
|
||||
|
||||
func _on_search_and_replace_theme_changed() -> void:
|
||||
apply_theme()
|
||||
|
||||
|
||||
func _on_input_text_changed(new_text: String) -> void:
|
||||
search(new_text)
|
||||
|
||||
|
||||
func _on_previous_button_pressed() -> void:
|
||||
self.result_index = wrapi(result_index - 1, 0, results.size())
|
||||
|
||||
|
||||
func _on_next_button_pressed() -> void:
|
||||
self.result_index = wrapi(result_index + 1, 0, results.size())
|
||||
|
||||
|
||||
func _on_search_and_replace_visibility_changed() -> void:
|
||||
if is_instance_valid(input):
|
||||
if visible:
|
||||
input.grab_focus()
|
||||
var selection = code_edit.get_selected_text()
|
||||
if input.text == "" and selection != "":
|
||||
input.text = selection
|
||||
search(selection)
|
||||
else:
|
||||
search()
|
||||
else:
|
||||
input.text = ""
|
||||
|
||||
|
||||
func _on_input_gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.is_pressed():
|
||||
match event.as_text():
|
||||
"Enter":
|
||||
if results.size() == 0:
|
||||
search(input.text)
|
||||
self.result_index = wrapi(result_index + 1, 0, results.size())
|
||||
"Escape":
|
||||
emit_signal("close_requested")
|
||||
|
||||
|
||||
func _on_replace_button_pressed() -> void:
|
||||
if result_index == -1: return
|
||||
|
||||
# Replace the selection at result index
|
||||
var r: Array = results[result_index]
|
||||
code_edit.begin_complex_operation()
|
||||
var lines: PackedStringArray = code_edit.text.split("\n")
|
||||
var line: String = lines[r[0]]
|
||||
line = line.substr(0, r[1]) + replace_input.text + line.substr(r[1] + r[2])
|
||||
lines[r[0]] = line
|
||||
code_edit.text = "\n".join(lines)
|
||||
code_edit.end_complex_operation()
|
||||
code_edit.text_changed.emit()
|
||||
|
||||
search(input.text, result_index)
|
||||
|
||||
|
||||
func _on_replace_all_button_pressed() -> void:
|
||||
if match_case_button.button_pressed:
|
||||
code_edit.text = code_edit.text.replace(input.text, replace_input.text)
|
||||
else:
|
||||
code_edit.text = code_edit.text.replacen(input.text, replace_input.text)
|
||||
search()
|
||||
code_edit.text_changed.emit()
|
||||
|
||||
|
||||
func _on_replace_check_button_toggled(button_pressed: bool) -> void:
|
||||
replace_panel.visible = button_pressed
|
||||
if button_pressed:
|
||||
replace_input.grab_focus()
|
||||
|
||||
|
||||
func _on_input_focus_entered() -> void:
|
||||
if results.size() == 0:
|
||||
search()
|
||||
else:
|
||||
self.result_index = result_index
|
||||
|
||||
|
||||
func _on_match_case_check_box_toggled(button_pressed: bool) -> void:
|
||||
search()
|
||||
|
||||
|
||||
#endregion
|
||||
@@ -0,0 +1 @@
|
||||
uid://cijsmjkq21cdq
|
||||
@@ -0,0 +1,87 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://gr8nakpbrhby"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cijsmjkq21cdq" path="res://addons/dialogue_manager/components/search_and_replace.gd" id="1_8oj1f"]
|
||||
|
||||
[node name="SearchAndReplace" type="VBoxContainer"]
|
||||
visible = false
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 31.0
|
||||
grow_horizontal = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_8oj1f")
|
||||
|
||||
[node name="Search" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Input" type="LineEdit" parent="Search"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Text to search for"
|
||||
metadata/_edit_use_custom_anchors = true
|
||||
|
||||
[node name="MatchCaseCheckBox" type="CheckBox" parent="Search"]
|
||||
layout_mode = 2
|
||||
text = "Match case"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Search"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PreviousButton" type="Button" parent="Search"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Previous"
|
||||
flat = true
|
||||
|
||||
[node name="ResultLabel" type="Label" parent="Search"]
|
||||
layout_mode = 2
|
||||
text = "0 of 0"
|
||||
|
||||
[node name="NextButton" type="Button" parent="Search"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Next"
|
||||
flat = true
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="Search"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ReplaceCheckButton" type="CheckButton" parent="Search"]
|
||||
layout_mode = 2
|
||||
text = "Replace"
|
||||
|
||||
[node name="Replace" type="HBoxContainer" parent="."]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ReplaceLabel" type="Label" parent="Replace"]
|
||||
layout_mode = 2
|
||||
text = "Replace with:"
|
||||
|
||||
[node name="Input" type="LineEdit" parent="Replace"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ReplaceButton" type="Button" parent="Replace"]
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Replace"
|
||||
flat = true
|
||||
|
||||
[node name="ReplaceAllButton" type="Button" parent="Replace"]
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Replace all"
|
||||
flat = true
|
||||
|
||||
[connection signal="theme_changed" from="." to="." method="_on_search_and_replace_theme_changed"]
|
||||
[connection signal="visibility_changed" from="." to="." method="_on_search_and_replace_visibility_changed"]
|
||||
[connection signal="focus_entered" from="Search/Input" to="." method="_on_input_focus_entered"]
|
||||
[connection signal="gui_input" from="Search/Input" to="." method="_on_input_gui_input"]
|
||||
[connection signal="text_changed" from="Search/Input" to="." method="_on_input_text_changed"]
|
||||
[connection signal="toggled" from="Search/MatchCaseCheckBox" to="." method="_on_match_case_check_box_toggled"]
|
||||
[connection signal="pressed" from="Search/PreviousButton" to="." method="_on_previous_button_pressed"]
|
||||
[connection signal="pressed" from="Search/NextButton" to="." method="_on_next_button_pressed"]
|
||||
[connection signal="toggled" from="Search/ReplaceCheckButton" to="." method="_on_replace_check_button_toggled"]
|
||||
[connection signal="focus_entered" from="Replace/Input" to="." method="_on_input_focus_entered"]
|
||||
[connection signal="gui_input" from="Replace/Input" to="." method="_on_input_gui_input"]
|
||||
[connection signal="pressed" from="Replace/ReplaceButton" to="." method="_on_replace_button_pressed"]
|
||||
[connection signal="pressed" from="Replace/ReplaceAllButton" to="." method="_on_replace_all_button_pressed"]
|
||||
@@ -0,0 +1,69 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
signal title_selected(title: String)
|
||||
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
|
||||
|
||||
@onready var filter_edit: LineEdit = $FilterEdit
|
||||
@onready var list: ItemList = $List
|
||||
|
||||
var titles: PackedStringArray:
|
||||
set(next_titles):
|
||||
titles = next_titles
|
||||
apply_filter()
|
||||
get:
|
||||
return titles
|
||||
|
||||
var filter: String:
|
||||
set(next_filter):
|
||||
filter = next_filter
|
||||
apply_filter()
|
||||
get:
|
||||
return filter
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
apply_theme()
|
||||
|
||||
filter_edit.placeholder_text = DialogueConstants.translate(&"titles_list.filter")
|
||||
|
||||
|
||||
func select_title(title: String) -> void:
|
||||
list.deselect_all()
|
||||
for i in range(0, list.get_item_count()):
|
||||
if list.get_item_text(i) == title.strip_edges():
|
||||
list.select(i)
|
||||
|
||||
|
||||
func apply_filter() -> void:
|
||||
list.clear()
|
||||
for title in titles:
|
||||
if filter == "" or filter.to_lower() in title.to_lower():
|
||||
list.add_item(title.strip_edges())
|
||||
|
||||
|
||||
func apply_theme() -> void:
|
||||
if is_instance_valid(filter_edit):
|
||||
filter_edit.right_icon = get_theme_icon("Search", "EditorIcons")
|
||||
if is_instance_valid(list):
|
||||
list.add_theme_stylebox_override("panel", get_theme_stylebox("panel", "Panel"))
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_theme_changed() -> void:
|
||||
apply_theme()
|
||||
|
||||
|
||||
func _on_filter_edit_text_changed(new_text: String) -> void:
|
||||
self.filter = new_text
|
||||
|
||||
|
||||
func _on_list_item_clicked(index: int, at_position: Vector2, mouse_button_index: int) -> void:
|
||||
if mouse_button_index == MOUSE_BUTTON_LEFT:
|
||||
var title = list.get_item_text(index)
|
||||
title_selected.emit(title)
|
||||
@@ -0,0 +1 @@
|
||||
uid://d0k2wndjj0ifm
|
||||
@@ -0,0 +1,27 @@
|
||||
[gd_scene format=3 uid="uid://ctns6ouwwd68i"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d0k2wndjj0ifm" path="res://addons/dialogue_manager/components/title_list.gd" id="1_5qqmd"]
|
||||
|
||||
[node name="TitleList" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_5qqmd")
|
||||
|
||||
[node name="FilterEdit" type="LineEdit" parent="."]
|
||||
layout_mode = 2
|
||||
placeholder_text = "Filter titles"
|
||||
clear_button_enabled = true
|
||||
|
||||
[node name="List" type="ItemList" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
allow_reselect = true
|
||||
|
||||
[connection signal="theme_changed" from="." to="." method="_on_theme_changed"]
|
||||
[connection signal="text_changed" from="FilterEdit" to="." method="_on_filter_edit_text_changed"]
|
||||
[connection signal="item_clicked" from="List" to="." method="_on_list_item_clicked"]
|
||||
@@ -0,0 +1,125 @@
|
||||
@tool
|
||||
extends Button
|
||||
|
||||
const DialogueConstants = preload("../constants.gd")
|
||||
const DialogueSettings = preload("../settings.gd")
|
||||
|
||||
const REMOTE_RELEASES_URL = "https://api.github.com/repos/nathanhoad/godot_dialogue_manager/releases"
|
||||
|
||||
|
||||
@onready var http_request: HTTPRequest = $HTTPRequest
|
||||
@onready var download_dialog: AcceptDialog = $DownloadDialog
|
||||
@onready var download_update_panel = $DownloadDialog/DownloadUpdatePanel
|
||||
@onready var needs_reload_dialog: AcceptDialog = $NeedsReloadDialog
|
||||
@onready var update_failed_dialog: AcceptDialog = $UpdateFailedDialog
|
||||
@onready var timer: Timer = $Timer
|
||||
|
||||
var needs_reload: bool = false
|
||||
|
||||
# A lambda that gets called just before refreshing the plugin. Return false to stop the reload.
|
||||
var on_before_refresh: Callable = func(): return true
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
hide()
|
||||
apply_theme()
|
||||
|
||||
# Check for updates on GitHub
|
||||
check_for_update()
|
||||
|
||||
# Check again every few hours
|
||||
timer.start(60 * 60 * 12)
|
||||
|
||||
|
||||
# Convert a version number to an actually comparable number
|
||||
func version_to_number(version: String) -> int:
|
||||
var bits = version.split(".")
|
||||
return bits[0].to_int() * 1000000 + bits[1].to_int() * 1000 + bits[2].to_int()
|
||||
|
||||
|
||||
func apply_theme() -> void:
|
||||
var color: Color = get_theme_color("success_color", "Editor")
|
||||
|
||||
if needs_reload:
|
||||
color = get_theme_color("error_color", "Editor")
|
||||
icon = get_theme_icon("Reload", "EditorIcons")
|
||||
add_theme_color_override("icon_normal_color", color)
|
||||
add_theme_color_override("icon_focus_color", color)
|
||||
add_theme_color_override("icon_hover_color", color)
|
||||
|
||||
add_theme_color_override("font_color", color)
|
||||
add_theme_color_override("font_focus_color", color)
|
||||
add_theme_color_override("font_hover_color", color)
|
||||
|
||||
|
||||
func check_for_update() -> void:
|
||||
if DialogueSettings.get_user_value("check_for_updates", true):
|
||||
http_request.request(REMOTE_RELEASES_URL)
|
||||
|
||||
|
||||
### Signals
|
||||
|
||||
|
||||
func _on_http_request_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||
if result != HTTPRequest.RESULT_SUCCESS: return
|
||||
|
||||
var current_version: String = DMPlugin.get_version()
|
||||
|
||||
# Work out the next version from the releases information on GitHub
|
||||
var response = JSON.parse_string(body.get_string_from_utf8())
|
||||
if typeof(response) != TYPE_ARRAY: return
|
||||
|
||||
# GitHub releases are in order of creation, not order of version
|
||||
var versions = (response as Array).filter(func(release):
|
||||
var version: String = release.tag_name.substr(1)
|
||||
var major_version: int = version.split(".")[0].to_int()
|
||||
var current_major_version: int = current_version.split(".")[0].to_int()
|
||||
return major_version == current_major_version and version_to_number(version) > version_to_number(current_version)
|
||||
)
|
||||
if versions.size() > 0:
|
||||
download_update_panel.next_version_release = versions[0]
|
||||
text = DialogueConstants.translate(&"update.available").format({ version = versions[0].tag_name.substr(1) })
|
||||
show()
|
||||
|
||||
|
||||
func _on_update_button_pressed() -> void:
|
||||
if needs_reload:
|
||||
var will_refresh = on_before_refresh.call()
|
||||
if will_refresh:
|
||||
EditorInterface.restart_editor(true)
|
||||
else:
|
||||
var scale: float = EditorInterface.get_editor_scale()
|
||||
download_dialog.min_size = Vector2(300, 250) * scale
|
||||
download_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_download_dialog_close_requested() -> void:
|
||||
download_dialog.hide()
|
||||
|
||||
|
||||
func _on_download_update_panel_updated(updated_to_version: String) -> void:
|
||||
download_dialog.hide()
|
||||
|
||||
needs_reload_dialog.dialog_text = DialogueConstants.translate(&"update.needs_reload")
|
||||
needs_reload_dialog.ok_button_text = DialogueConstants.translate(&"update.reload_ok_button")
|
||||
needs_reload_dialog.cancel_button_text = DialogueConstants.translate(&"update.reload_cancel_button")
|
||||
needs_reload_dialog.popup_centered()
|
||||
|
||||
needs_reload = true
|
||||
text = DialogueConstants.translate(&"update.reload_project")
|
||||
apply_theme()
|
||||
|
||||
|
||||
func _on_download_update_panel_failed() -> void:
|
||||
download_dialog.hide()
|
||||
update_failed_dialog.dialog_text = DialogueConstants.translate(&"update.failed")
|
||||
update_failed_dialog.popup_centered()
|
||||
|
||||
|
||||
func _on_needs_reload_dialog_confirmed() -> void:
|
||||
EditorInterface.restart_editor(true)
|
||||
|
||||
|
||||
func _on_timer_timeout() -> void:
|
||||
if not needs_reload:
|
||||
check_for_update()
|
||||
@@ -0,0 +1 @@
|
||||
uid://cr1tt12dh5ecr
|
||||
@@ -0,0 +1,42 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://co8yl23idiwbi"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cr1tt12dh5ecr" path="res://addons/dialogue_manager/components/update_button.gd" id="1_d2tpb"]
|
||||
[ext_resource type="PackedScene" uid="uid://qdxrxv3c3hxk" path="res://addons/dialogue_manager/components/download_update_panel.tscn" id="2_iwm7r"]
|
||||
|
||||
[node name="UpdateButton" type="Button"]
|
||||
visible = false
|
||||
offset_right = 8.0
|
||||
offset_bottom = 8.0
|
||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_hover_color = Color(0, 0, 0, 1)
|
||||
theme_override_colors/font_focus_color = Color(0, 0, 0, 1)
|
||||
text = "v2.9.0 available"
|
||||
flat = true
|
||||
script = ExtResource("1_d2tpb")
|
||||
|
||||
[node name="HTTPRequest" type="HTTPRequest" parent="."]
|
||||
|
||||
[node name="DownloadDialog" type="AcceptDialog" parent="."]
|
||||
title = "Download update"
|
||||
size = Vector2i(400, 300)
|
||||
unresizable = true
|
||||
min_size = Vector2i(300, 250)
|
||||
ok_button_text = "Close"
|
||||
|
||||
[node name="DownloadUpdatePanel" parent="DownloadDialog" instance=ExtResource("2_iwm7r")]
|
||||
|
||||
[node name="UpdateFailedDialog" type="AcceptDialog" parent="."]
|
||||
dialog_text = "You have been updated to version 2.4.3"
|
||||
|
||||
[node name="NeedsReloadDialog" type="ConfirmationDialog" parent="."]
|
||||
|
||||
[node name="Timer" type="Timer" parent="."]
|
||||
wait_time = 14400.0
|
||||
|
||||
[connection signal="pressed" from="." to="." method="_on_update_button_pressed"]
|
||||
[connection signal="request_completed" from="HTTPRequest" to="." method="_on_http_request_request_completed"]
|
||||
[connection signal="close_requested" from="DownloadDialog" to="." method="_on_download_dialog_close_requested"]
|
||||
[connection signal="failed" from="DownloadDialog/DownloadUpdatePanel" to="." method="_on_download_update_panel_failed"]
|
||||
[connection signal="updated" from="DownloadDialog/DownloadUpdatePanel" to="." method="_on_download_update_panel_updated"]
|
||||
[connection signal="confirmed" from="NeedsReloadDialog" to="." method="_on_needs_reload_dialog_confirmed"]
|
||||
[connection signal="timeout" from="Timer" to="." method="_on_timer_timeout"]
|
||||
Reference in New Issue
Block a user